I've followed django tutorial and arrived at tutorial05.
I tried to not show empty poll as tutorial says, so I added filter condition like this:
class IndexView(generic.ListView):
...
def get_queryset(self):
return Question.objects.filter(
pub_date__lte=timezone.now(),
choice__isnull=False
).order_by('-pub_date')[:5]
But this returned two objects which are exactly same.
I think choice__isnull=False caused the problem, but not sure.
choice__isnull causes the problem. It leads to join with choice table (to weed out questions without choices), that is something like this:
SELECT question.*
FROM question
JOIN choice
ON question.id = choice.question_id
WHERE question.pub_date < NOW()
You can inspect query attribute of QuerySet to be sure. So if you have one question with two choices, you will get that question two times. You need to use distinct() method in this case: queryset.distinct().
Just use .distinct() at the end of your ORM.
A little late to the party, but I figured it could help others looking up the same issue.
Instead of using choice__isnull=False with the filter() method, use it with exclude() instead to exclude out any questions without any choices. So your code would look something like this:
...
def get_queryset(self):
return Question.objects.filter(pub_date__lte=timezone.now()).exclude(choice__isnull=True).order_by('-pub_date')[:5]
By doing it this way, it will return only one instance of the question. Be sure to use choice_isnull=True though.
Because you created two objects with same properties. If you want to ensure uniqueness, you should add validation in clean and add unique index on identifier field too.
Besides filter returns all the objects that match the criteria, if you are expecting only one item to be returned, you should use get instead. get would raise exception if less or more than 1 item is found.
Related
I know in django we can create an object model easily by using something like this
AbcModel.objects.create(field1='a', field2='b')
But this would create the object even if it already exists.
I know I can use filter() then use the exist() to check if the object already exist then decide to update or create.
But is there an easier and faster way to do this? Since, there is get_or_create so I am curious if there's something similar.
Thanks in advance
EDIT:
I thought of something like this
new = AbcModel.objects.create(field1='a')
new[0].field2 = 'c'
new[0].save()
There might be more fields and field1 will not always be a would be others like b, c and maybe a again.
Just being curious if there is an easier faster way and not saying get_or_create wouldn't get what I want / need
As you said about get_or_create, you could do:
abc_instance, created = AbcModel.objects.get_or_create(field1='a', field2='b')
That would bring you the existent/created object as the first argument and a boolean as the second argument, that defines if it was got or created.
Additionally, field1 and field2 will be used for the filter, but you can set the defaults attribute, which will update the existing entry or be used for its creation.
abc_instance, created = AbcModel.objects.get_or_create(
field1='a', field2='b',
defaults={'field3': 'c', 'field4': 'd'}
)
You can use update_or_create: https://docs.djangoproject.com/en/2.0/ref/models/querysets/#update-or-create
However if the field you filter by is not unique, you might end up getting a MultipleObjectsReturned exception.
Another way to do this could be:
num_updated = AbcModel.objects.filter(field1='a').update(field2='c')
if num_updated == 0:
model = AbcModel.objects.create(field1='a', field2='c')
In num_updated you will have the number of rows updated in the first line of code.
I hope this helps a bit!
I am extending product.template, and I've added a field called uom_class. When I change this field when editing a product, I'd like to clear what is entered in the Many2One field called "Unit of Measure" (uom_id in product.template). I am doing this because I am also changing the domain for the uom_id, and the existing selection ("Units" by default) will probably not be in that domain.
I've tried the following, as suggested for earlier versions, but it did not work.
#api.onchange('uom_class')
def onchange_uom_class(self):
# [...] not shown: previously set new domain for uom_id
result['value'] ={'uom_id':False}
return result
I've seen other posts suggest I need to add assign it an empty product.uom record, but I have no idea how to do that. Any help would be greatly appreciated.
Well, I figured this one out.
I just declared
uom_id = False
For some reason, returning the domain works, but not returning the value. Either that, or I just have no idea what I'm doing and I'm returning the value wrong... which is entirely possible.
While going through the Django tutorial, I've decided to implement some "ideas for more tests" in part 5, namely
For example, it’s silly that Questions can be published on the site that have no Choices. So, our views could check for this, and exclude such Questions.
I've implemented this check in views.py by changing the IndexView.get_queryset function as follows
def get_queryset(self):
#Return the last five published questions with nonzero choice_set
result = [];
for q in Question.objects.all():
if q.choice_set.count() > 0:
result.append(q)
return sorted( result, key=attrgetter('pub_date'), reverse=True )[0:5]
Is there a more elegant way of doing this? Something along the lines of return Question.objects.filter(choice_set.count()>0).order_by('-pub_date')[:5] (which does not work for probably obvious reasons).
EDIT 1
Another possible answer would be return Question.objects.filter(pk__in=[x.question.pk for x in Choice.objects.all()]).order_by('-pub_date')[:5] (inspired by this). It still won't allow exclusion of questions with a single choice, like #catavaran suggested.
You can exclude all questions where choice=None, and it will exclude all questions without choices:
qs = Question.objects.exclude(choice=None).order_by'(-pub_date')[:5]
Or you can use choice__isnull=False, but this can return duplicate entries. Use .distinct() to counter that:
qs = Question.objects.filter(choice__isnull=False).distinct()
You can use aggregation:
from django.db.models import Count
Question.objects.annotate(num_choices=Count('choice')) \
.filter(num_choices__gt=0).order_by('-pub_date')[:5]
In fact poll with only one choice does not makes sense too :-) So it is better to use filter(num_choices__gt=1) instead of filter(num_choices__gt=0).
Can we use
MyClass.objects.get(description='hi').exclude(status='unknown')
Your code works as expected if you do the exclude() before the get():
MyClass.objects.exclude(status='unknown').get(description='hi')
As #Burhan Khalid points out, the call to .get will only succeed if the resulting query returns exactly one row.
You could also use the Q object to get specify the filter directly in the .get:
MyClass.objects.get(Q(description='hi') & ~Q(status='unknown'))
Note that the Q object is only necessary because you use a .exclude (and Django's ORM does not have a not equal field lookup so you have to use .exclude).
If your original code had been (note that .exclude has been replaced with .filter):
MyClass.objects.filter(status='unknown').get(description='hi')
... you could simply do:
MyClass.objects.get(status='unknown', description='hi')
You want instead:
MyClass.objects.filter(description='hi').exclude(status='unknown')
.get() will raise MultipleObjectsReturned if your query results in more than one matching set; which is likely to happen considering you are searching on something that isn't a primary key.
Using filter will give you a QuerySet, which you can later chain with other methods or simply step through to get the results.
If I want to check for the existence and if possible retrieve an object, which of the following methods is faster? More idiomatic? And why? If not either of the two examples I list, how else would one go about doing this?
if Object.objects.get(**kwargs).exists():
my_object = Object.objects.get(**kwargs)
my_object = Object.objects.filter(**kwargs)
if my_object:
my_object = my_object[0]
If relevant, I care about mysql and postgres for this.
Why not do this in a try/except block to avoid the multiple queries / query then an if?
try:
obj = Object.objects.get(**kwargs)
except Object.DoesNotExist:
pass
Just add your else logic under the except.
django provides a pretty good overview of exists
Using your first example it will do the query two times, according to the documentation:
if some_queryset has not yet been evaluated, but you
know that it will be at some point, then using some_queryset.exists()
will do more overall work (one query for the existence check plus an
extra one to later retrieve the results) than simply using
bool(some_queryset), which retrieves the results and then checks if
any were returned.
So if you're going to be using the object, after checking for existance, the docs suggest just using it and forcing evaluation 1 time using
if my_object:
pass