[Update: software versions Python 2.7.2, Django 1.3.1]
Can anyone explain this console code?
FinishingStep has a ForeignKey to a quote object, but that's not really relevant.
>>> fins = FinishingStep.objects.filter(quote=jq)
>>> fins
[<FinishingStep: Tabbing>, <FinishingStep: Collator>]
So far so good, we have returned a QuerySet with two objects.
But now the confusion. Both objects now appear to be the same:
>>> fins[0]
<FinishingStep: Collator>
>>> fins[1]
<FinishingStep: Collator>
Convert it to a list, and that fixes it.
>>> fins = list(fins)
>>> fins
[<FinishingStep: Tabbing>, <FinishingStep: Collator>]
>>> fins[0]
<FinishingStep: Tabbing>
>>> fins[1]
<FinishingStep: Collator>
[Update: Adding .distinct() to the query also fixes it. This especially odd since, at the moment, there are only those two items in the database.]
Is this a bug? Am I doing something wrong?
This ticket discusses this behavior: https://code.djangoproject.com/ticket/9006
Just use order_by query. This happens because the database engine is free to return any suitable row if you do not specify explicit ordering. So I guess it just picks the one from its cache.
Related
I've seen Django 2.0 consists of FilteredRelation object in queryset. What is the usage of newly introduced FilteredRelation?
What I've looked into?
I observed Django 2.0 Documentation but I could not understand idea behind this FilteredRelation object.
I looked into following code. But I didn't get it.
>>> from django.db.models import FilteredRelation, Q
>>> Restaurant.objects.annotate(
... pizzas_vegetarian=FilteredRelation(
... 'pizzas', condition=Q(pizzas__vegetarian=True),
... ),
... ).filter(pizzas_vegetarian__name__icontains='mozzarella')
Main Question
Show now my question is that what is usage of FilteredRelation and when to use in your QuerySet?
I think the documentation itself self-explanatory.
You could achieve the same result in,
Method-1
from django.db.models import FilteredRelation, Q
result_1 = Restaurant.objects.annotate(pizzas_vegetarian=FilteredRelation('pizzas', condition=Q(pizzas__vegetarian=True), ), ).filter(
pizzas_vegetarian__name__icontains='mozzarella')
Method-2
result_2 = Restaurant.objects.filter(pizzas__vegetarian=True, pizzas__name__icontains='mozzarella')
You will get better performance with Method-1 since the filtering in the WHERE clause of the first queryset will only operate on vegetarian pizzas.
UPDATE
The Django #29555 ticket has more information regarding the usage and performance.
The FilteredRelation() not only improves performance but also creates
correct results when aggregating with multiple LEFT JOINs.
When the docs discuss values() and values_list(), or any query for that matter, they always require that you know what you are looking for, i.e.,
>>> Entry.objects.values_list('headline', flat=True).get(pk=1)
'First entry'
What about the situation where you need a value from a specific field, whether on this model or a foreign key, but you don't know the pk or the value in the specified field, and you don't care, you just need whatever is there. How do you query for it?
Alternatively, if I use this example from the docs:
>>> Entry.objects.values_list('id', flat=True).order_by('id')
<QuerySet [1, 2, 3, ...]>
Could I add slice notation to the end of the query? But even then, I might not know in advance which slice I need. In other words, how to dynamically get a value from a specified field without knowing in advance what it or its pk is? Thx.
Depending your scenario (this time a simple query) you have many of options to do it. One is to use a variable as field name. Then, feed that variable dynamically:
>>> field='headline'
>>> Entry.objects.values_list(field, flat=True).get(pk=1)
'First entry'
>>> field='body'
>>> Entry.objects.values_list(field, flat=True).get(pk=1)
'First entry body'
In order to slice results use offset/limit as follows:
Entry.objects.all()[offset:limit]
>>> field='headline'
>>> Entry.objects.values_list(field, flat=True)[5:10]
I'm not really sure what's happening here. I can see that I have a couple of emails in my database, but I can't seem to filter them.
for example, when I run
qs1 = EmailActivation.objects.all()
>>> print(qs1)
<EmailActivationQuerySet [<EmailActivation: a#yahoo.com>, <EmailActivation: b#gmail.com>]>
however, when I run the following I get nothing
qs2 = EmailActivation.objects.all().filter(email='a#yahoo.com')
>>> print(qs2)
<EmailActivationQuerySet []>
my model looks like this:
class EmailActivation(models.Model):
user = models.ForeignKey(User)
email = models.EmailField()
I'd expect my qs2 to return 'a#yahoo.com' since it is in the database as seen by qs1. Does someone see what I'm doing wrong?
Thanks,
Edit: looking closer I see the following:
qs2 =
EmailActivation.objects.all().filter(email__icontains='a#yahoo.com')
>>> print(qs2)
<EmailActivationQuerySet [<EmailActivation: a#yahoo.com>]>
Does this mean that there is some whitespace or hidden character in my 'email'? I imagine filtering with icontains would be bad as someone could be 'aa#yahoo.com'. What could I do to strip whatever hidden character is there?
If you explicitly specify exact match on the filter then you should get a result what you are after. I don't think so there are any hidden characters here...
>>> EmailActivation.objects.all().filter(email__exact='a#yahoo.com')
<QuerySet [<EmailActivation: EmailActivation object (1)>]>
I have the following models.py:
class BagOfApples(models.Model):
quantity = models.PositiveSmallIntegerField(default = 0)
Let's create a “bag of apples” object and put an “apple” in it:
>>> from myapp import models
>>>
>>> models.BagOfApples().save()
>>>
>>> bag1 = models.BagOfApples.objects.get(pk = 1)
>>> bag2 = models.BagOfApples.objects.get(pk = 1)
>>>
>>> bag1.quantity += 1
>>> bag1.save()
>>>
>>> bag1.quantity
1
>>> bag2.quantity
0
Is there a way to automatically reload the data in the bag2 variable?
In my real-world application, things are a bit more complicated than that, and the same objects can be modified in different parts of the code after being retrieved by different database queries. This is not even intentional (for instance, this problem occurs to me because Django caches results for OneToOne or ForeignKey relationships).
It would be really helpful to have some kind of manager (maybe a middleware can do this?) that keeps track of the identical objects.
In this example, the manager would automatically detect that bag2 is actually bag1, and would not execute a second query but simply return bag1.
If there is no good way to do this, any other helpful tips on better application design would be appreciated.
The key is to understand when your query is actually executed. The model object will not repeat the query each time you want to access one of its model fields.
To avoid keeping around objects with stale data, I would make sure to query when I actually need to data. In your example, I'd hold on to the information that you know is not going to change (the pk value), and do a get when you need the object with fresh values.
from myapp import models
models.BagOfApples().save()
bag1 = models.BagOfApples.objects.get(pk = 1)
bag1.quantity += 1
bag1.save()
print bag1.quantity # will print 1
bag2 = models.BagOfApples.objects.get(pk = 1)
print bag2.quantity # will print 1 too
I have effectively a cached foreign key field in one of my indexes, called thingy_ids, which is just a comma separated list of ids with thingy_id_ in front of them. I want to use this like this:
>>> sqs.filter(thingy_ids="thingy_id_133932,")
To get results that have that thingy attached to them.
This works on my local machine, but not on heroku. On heroku, here are the baffling results:
>>> sqs = SearchQuerySet()
>>> sqs[0].thingy_ids
u'thingy_id_133932,thingy_id_133923,'
>>> sqs.filter(thingy_ids="thingy_id_133932")
[]
In fact, no filter on thingy_ids seems to work:
>>> sqs.filter(thingy_ids="1")
[]
>>> sqs.filter(thingy_ids="thingy_id")
[]
Edit:
Ah hah! Adding a space between the comma separated list seems to fix the problem.
I leave this question open because there's probably a better way to cache this foreign key relation in the index and search for it. Thoughts?
Adding spaces between the commas fixed this problem, though I'm still unsure why.