Wednesday 14 October 2015

Django query in detail

To UPDATE data in database using GET and FILTER

problem:
 Why does objects.filter not updating the field but objects.get works?

I have a Django model :
class QuestionAnswer(models.Model):
   question = models.ForeignKey(Question)
   marks = models.FloatField(null=True)
   graded = models.IntegerField()
Now in command line I do:

>>> qa = QuestionAnswer.objects.filter(pk=12345)
>>> qa[0].graded 
0
>>> qa[0].graded = 1
>>> qa[0].save()
>>> qa = QuestionAnswer.objects.filter(pk=12345)
>>> qa.graded
0
The graded field is not updated.
But when I do:
>>> qa = QuestionAnswer.objects.get(pk=12345)
>>> qa.graded 
0
>>> qa.graded = 1
>>> qa.save()
>>> qa = QuestionAnswer.objects.get(pk=12345)
>>> qa.graded
1
Why does objects.filter not updating the field but objects.get works.

Solution: 
try this solution


>>> qs = QuestionAnswer.objects.filter(pk=12345)
>>> qa = qs[0]
>>> qa.graded
0
>>> qa.graded = 1
>>> qa.save()
>>> qa = QuestionAnswer.objects.filter(pk=12345)
>>> qa[0].graded
This should be 1
By using qa[0], you're not actually modifying and saving the same object (even though they represent the same SQL data).
This is due to the fact that querysets are lazy: they only execute the sql query when you actually try to use the data that the queryset would return. The way slices work with querysets, the query is executed but the result will not be cached. This means that whenever you use qa[0], a new query is executed, and that data is saved in a newly created model instance. What you are effectively doing is this:
>>> qs = QuestionAnswer.objects.filter(pk=12345)
>>> qa1 = qs.get()
>>> qa1.graded
0
>>> qa2 = qs.get()
>>> qa2.graded = 1
>>> qa3 = qs.get()
>>> qa3.save()
It should be obvious that qa1qa2 and qa3 are different instances of your model: while they have the same attribute values (as the represent the same database data), they are actually saved in different places in memory and are completely separate from each other. Changing the gradedattribute on qa2 will not in any way affect qa3, so when qa3 is saved, the changes to qa2won't be reflected in the changes in the database.
However, if you were to evaluate the entire queryset before slicing, all results would be cached, and the following would work:
>>> qs = QuestionAnswer.objects.filter(pk=12345)
>>> qs[0] is qs[0]
False # These are not the same objects in memory...
>>> bool(qs) # This forces evaluation of the entire queryset
True
>>> qs[0] is qs[0]
True # ... but these are!
>>> qs[0].graded
0
>>> qs[0].graded = 1
>>> qs[0].save()
>>> qs = QuestionAnswer.objects.filter(pk=12345)
>>> qs[0].graded
1
Using Filter

Filter : 
1.It can store more than one result in the list.so where we want retrieve more than one data at same id there we use Filter. 
2. It is Used as Where(like sql) in Django.
3.filter returns a QuerySet (even though it looks like a list). In django, querysets are lazy which means they are evaluated only when needed.
4. if object is not found then it will not raise error.

Try out this when you use filter in ORM query to update.
user = User.objects.filter(email = 'bhartirawat@gmail.com').update(is_email_verified=True)

or

user = User.objects.filter(email = 'bhartirawat@gmail.com')
user = user[0]
user.is_email_verified=True
In filter we have no need to save filter like user.save(). It automatically save the data in the database.

Note: filter equal to where (in sql)

Filter give only one result whereas Get give one or more than one result depending upon the data in database which is to be found

Using Get

GET:
1. It is also same as Filter. It is also Used as Where(like sql) in Django. But It Cann't Store more than one result.

2.get returns the actual object which is not "lazy"; which is why your changes seem to be effected immediately

3. if object is not found then it will raise error.

user = User.objects.get(email = 'bhartirawat@gmail.com')
user.is_email_verified=True
user.save()
 Django will complain if more than one item matches the get() query. In this case, it will raiseMultipleObjectsReturned, which again is an attribute of the model class itself.


Limiting QuerySets

This is the equivalent of SQL’s LIMIT and OFFSET clauses.
For example, this returns the first 5 objects (LIMIT 5):
>>> Entry.objects.all()[:5]
This returns the sixth through tenth objects (OFFSET 5 LIMIT 5):
>>> Entry.objects.all()[5:10]
Negative indexing (i.e. Entry.objects.all()[-1]) is not supported.
Generally, slicing a QuerySet returns a new QuerySet – it doesn’t evaluate the query. An exception is if you use the “step” parameter of Python slice syntax. For example, this would actually execute the query in order to return a list of every second object of the first 10:
>>> Entry.objects.all()[:10:2]
To retrieve a single object rather than a list (e.g. SELECT foo FROM bar LIMIT 1), use a simple index instead of a slice. For example, this returns the first Entry in the database, after ordering entries alphabetically by headline:
>>> Entry.objects.order_by('headline')[0]
This is roughly equivalent to:
>>> Entry.objects.order_by('headline')[0:1].get()
Note, however, that the first of these will raise IndexError while the second will raise DoesNotExist if no objects match the given criteria. See get() for more details..

3. get_or_create

it return a tuple -->> (object, created)
object -->> where object is the retrieved or created object 
created -->>  created is a boolean specifying whether a new object was created.
This is meant as a shortcut to boilerplatish code. For example:
try: obj = Person.objects.get(first_name='John', last_name='Lennon')except Person.DoesNotExist: obj = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9)) obj.save()
The above example can be rewritten using get_or_create() like so:
obj, created = Person.objects.get_or_create(first_name='John', last_name='Lennon',
                  defaults={'birthday': date(1940, 10, 9)})
important:
1. If an object is found, get_or_create() returns a tuple of that object and False.
2.  If multiple objects are found, get_or_create raises MultipleObjectsReturned.
3. If an object is not found, get_or_create() will instantiate and save a new object, returning a tuple of the new object and True.
4.The get_or_create() method has similar error behavior to create() when you’re using manually specified primary keys. If an object needs to be created and the key already exists in the database, an IntegrityError will be raised. see this in below example.
5. Finally, a word on using get_or_create() in Django views. Please make sure to use it only in POST requests unless you have a good reason not to. GET requests shouldn’t have any effect on data. Instead, use POST whenever a request to a page has a side effect on your data

6. The new object will be created roughly according to this algorithm:
params = {k: v for k, v in kwargs.items() if '__' not in k}
params.update(defaults)
obj = self.model(**params)
obj.save()
Example:

You can use get_or_create() through Book’s chapters field, but it only fetches inside the context of that book:
>>> book = Book.objects.create(title="Ulysses")
>>> book.chapters.get_or_create(title="Telemachus")
(<Chapter: Telemachus>, True)
>>> book.chapters.get_or_create(title="Telemachus")
(<Chapter: Telemachus>, False)
>>> Chapter.objects.create(title="Chapter 1")
<Chapter: Chapter 1>
>>> book.chapters.get_or_create(title="Chapter 1")
# Raises IntegrityError

This is happening because it’s trying to get or create “Chapter 1” through the book “Ulysses”, but it can’t do any of them: the relation can’t fetch that chapter because it isn’t related to that book, but it can’t create it either because title field should be unique.

Field lookups or django query in SQL


Field lookups are how you specify the meat of an SQL WHERE clause. They’re specified as keyword arguments to theQuerySet methods filter()exclude() and get().
>>> Entry.objects.filter(pub_date__lte='2006-01-01')
translates (roughly) into the following SQL
select * from blog_entry where pub_date <= '2006-01-01';

>>> Entry.objects.get(headline__exact="Man bites dog")
    SELECT * from blog_entry WHERE headline = 'Man bites dog';



No comments:

Post a Comment