I have some models like that;
class Ingredient(models.Model):
name = models.CharField(max_length=128)
class Recipe(models.Model):
name = models.CharField(max_length=128)
ingredients = models.ManyToManyField(Ingredient, through='RecipeIngredient')
class RecipeIngredient(models.Model):
recipe = models.ForeignKey(Recipe)
ingredient = models.ForeignKey(Ingredient)
How can I find the recipes that contains all Ingredients?
Here is an example;
>>> i1 = Ingredient(name="egg")
>>> i1.save()
>>> i2 = Ingredient(name="flour")
>>> i2.save()
>>> i3 = Ingredient(name="water")
>>> i3.save()
>>> i4 = Ingredient(name="milk")
>>> i4.save()
>>> i5 = Ingredient(name="sugar")
>>> i5.save()
>>> i6 = Ingredient(name="carrot")
>>> i6.save()
>>> i7 = Ingredient(name="wheat")
>>> i7.save()
>>> r1 = Recipe(name="omelet")
>>> r1.save()
>>> r1.ingredients.add(i1, i2, i3)
>>> r2 = Recipe(name="icecream")
>>> r2.save()
>>> r2.ingredients.add(i3, i4, i5)
>>> test = Recipe.objects.filter(ingredients__in=[i1.id, i2.id, i3.id, i4.id])
>>> test
[<Recipe: omelet>, <Recipe: omelet>, <Recipe: omelet>, <Recipe: icecream>, <Recipe: icecream>]
I want to find only omelet. Because only omelet contains egg (i1), flour (i2), water (i3)
Thanks
maybe chain it together?
Recipe.objects.filter(ingredients=i1.id).filter(ingredients=i2.id).filter(ingredients=i3.id).filter(ingredients=i4.id)
Related
I have such models:
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
tags = models.ManyToManyField(to="tag", related_name="tags", blank=True)
def __str__(self):
return self.title
class Tag(models.Model):
value = models.CharField(max_length=50)
parent = models.ManyToManyField("Tag", related_name="children", blank=True)
image = models.ImageField(upload_to="tags", null=True, blank=True)
def __str__(self):
return self.value
And I have an object, for example, post C which has tag 2 and tag 3
Currently I cannot create a combined ( q1 & q2 ) query, where I will be able to filter by that condition.
I am using combined queries because the case is more complex(so double filter won't do), I want to be able to filter by such queries as " 2 or ( 3 and 4 ) ", "(1 or 2) and (3 or 4)"
>>> q1
<Q: (AND: ('tags__in', '2'))>
>>> q2
<Q: (AND: ('tags__in', '3'))>
>>> Post.objects.filter(q1)
<QuerySet [<Post: post_B>, <Post: post C>, <Post: post D>]>
>>> Post.objects.filter(q2)
<QuerySet [<Post: post C>, <Post: post D>]>
>>> Post.objects.filter(q1 & q2)
<QuerySet []>
>>> str(Post.objects.filter(q1).query)
'SELECT "posts_post"."id", "posts_post"."title", "posts_post"."content" FROM "posts_post" INNER JOIN "posts_post_tags" ON ("posts_post"."id" = "posts_post_tags"."post_id") WHERE "posts_post_tags"."tag_id" IN (2)'
>>> str(Post.objects.filter(q2).query)
'SELECT "posts_post"."id", "posts_post"."title", "posts_post"."content" FROM "posts_post" INNER JOIN "posts_post_tags" ON ("posts_post"."id" = "posts_post_tags"."post_id") WHERE "posts_post_tags"."tag_id" IN (3)'
>>> str(Post.objects.filter(q1 & q2).query)
'SELECT "posts_post"."id", "posts_post"."title", "posts_post"."content" FROM "posts_post" INNER JOIN "posts_post_tags" ON ("posts_post"."id" = "posts_post_tags"."post_id") WHERE ("posts_post_tags"."tag_id" IN (2) AND "posts_post_tags"."tag_id" IN (3))'
What I've found is https://stackoverflow.com/a/8637972/20685072
It says " ANDed Q objects would not work "
from django.db.models import Q
query = Q()
for the or condition //
query.add(Q(id__in=[1,2] | Q(id__in=[3,4], query.connector)
as you asked for 2 and 3 //
query.add(Q(id=2) & Q(id=3), query.connector)
data = Model.objects.filter(query)
depends what is the requirement you can modify it as per need
I resolved it using raw Sql queries, UNION and INTERSECT
I have models similar to the Django documentation's pizza example:
class Pizza(models.Model):
name = models.CharField()
toppings = models.ManyToManyField('Topping')
def __str__(self):
return self.name
class Topping(models.Model):
name = models.CharField()
def __str__(self):
return self.name
And a few toppings with the expected pizzas:
>>> pepperoni = Topping.objects.create(name='pepperoni')
>>> sausage = Topping.objects.create(name='sausage')
>>> pineapple = Topping.objects.create(name='pineapple')
>>> olives = Topping.objects.create(name='olives')
>>> p1 = Pizza.objects.create(name='Pepperoni')
>>> p1.toppings.add(pepperoni)
>>> p2 = Pizza.objects.create(name='Sausage')
>>> p2.toppings.add(sausage)
>>> p3 = Pizza.objects.create(name='Pepperoni and Sausage')
>>> p3.toppings.add(pepperoni)
>>> p3.toppings.add(sausage)
>>> p4 = Pizza.objects.create(name='Pepperoni and Olives')
>>> p4.toppings.add(pepperoni)
>>> p4.toppings.add(olives)
>>> p5 = Pizza.objects.create(name='Pepperoni and Sausage and Olives')
>>> p5.toppings.add(pepperoni)
>>> p5.toppings.add(sausage)
>>> p5.toppings.add(olives)
>>> ...
How can I create a query that will return only return pizzas that have a topping of pepperoni (p1), or sausage (p2), or both pepperoni or sausage (p3)? I do not want pizzas that include pepperoni, sausage, and something else (p5).
Something like this will include a pizza that has pepperoni and olives (p4), which I don't want:
>>> Pizza.objects.filter(toppings__in=[pepperoni, sausage])
I can create a list of all toppings except the two I want and use that as an exclusion:
>>> toppings_i_do_not_want = Topping.objects.exclude(name__in=['Pepperoni', ['Sausage'])
>>> toppings_i_want = Topping.objects.filter(name__in=['Pepperoni', ['Sausage'])
>>> Pizza.objects.filter(toppings__in=toppings_i_want).exclude(toppings_i_do_not_want)
That will result in what I want, but it seems like the performance of such a query would suffer greatly if I'm only interested in two toppings but I have to pass ~100,000 other toppings into the exclude filter.
Is there a better way?
We can count the number of toppings that are pepperoni or sausage and compare that with the total number of related toppings, if the two match, and the number is larger than 0, then we can return such pizza:
from django.db.models import Count, Q
Pizza.objects.annotate(
ntopping=Count('toppings')
).filter(
ntopping__gte=1,
ntopping=Count('toppings', filter=Q(toppings__in=[pepperoni, sausage]))
)
will exactly do what you want. It will return Pizza records for which "there exists a related topping that is in the list [pepperoni, sausage]". So for pizza's that have a pepperoni topping, a sausage topping, or both toppings.
The input I give to a DecimalField of WTFform in Flask application is being converted to a string. I need it be an int or float in order to perform any
mathematical operation?
Any help would be appreciated.
TIA
wtforms.DecimalField produces a decimal.Decimal instance.
>>> import wtforms
>>> from webob.multidict import MultiDict
>>> class F(wtforms.Form):
... foo = wtforms.DecimalField()
...
>>> f = F(formdata=MultiDict(foo=3.45))
>>> val = f.data['foo']
>>> val
Decimal('3.45000000000000017763568394002504646778106689453125')
>>> type(val)
<class 'decimal.Decimal'>
The object can be used in calculations:
>>> val * 4
Decimal('13.80000000000000071054273576')
>>> val + 2
Decimal('5.450000000000000177635683940')
or can be converted to a float or int using the respective builtin functions
>>> float(val)
3.45
>>> int(val)
3
models.py
class People(models.Model):
name = models.CharField(max_length=16)
meters_away = models.IntegerField()
Lets populate the db:
>>> from people.models import People
>>> a = People()
>>> a.name = 'George'
>>> a.meters_away = 15
>>> a.save()
>>> b = People()
>>> b.name = 'Jim'
>>> b.meters_away = 10
>>> b.save()
Supposing that we have a url that returns all people in a range of x meters:
http://example.com/range/<meters>
This url scheme accepts 3 hits as follows:
http://example.com/range/20
http://example.com/range/30
http://example.com/range/40
Those hits will create the following queries:
>>> hit1 = People.objects.filter(meters_away__lt=20)
>>> hit2 = People.objects.filter(meters_away__lt=30)
>>> hit3 = People.objects.filter(meters_away__lt=40)
Where:
>>> list(hit1) == list(hit2) == list(hit3)
>>> True
This means that example.com, will serve 3 different urls with the same content.
From a SEO point of view, how could all the possible urls (meters: 21, 22, 23, 24, 30, 40 etc) be filtered in a way that a canonical url is appended to them?
The way i understood your question, you may want to get the maximum distance in meters that produce the same result as the current distance (say m meters):
next_number = People.objects.filter(meters_away__gte=m).order_by('meters_away')[:1]
next_number = next_number[0] if next_number else m
and the canonical url will be:
http://example.com/range/<next_number>
I have a base model and derived model from it. base model is not abstract, so it also has table associated with it.
Problem : I create a base instance first and then derived instance. And associate derived instance to base. But I get FileField in my derived class as None, even if its saved and available in parent. Why so? Am I missing something?
Some sample code:
def get_filepath(instance):
return u''+instance.name
def BaseModel(models.Model):
name = models.CharField(max_length=50)
filepath = models.FileField(upload_to=get_filepath,
max_length=255, null=True)
#some other fields
def DerivedModel(BaseModel):
type = models.CharField(max_length=50, null=True, blank=True)
Sample on django shell:
>>> obj = BaseModel.objects.create(name='y')
>>> obj.id
56
>>> obj.save()
>>> obj.id
56
>>> nf=ContentFile("this is dummy text")
>>> obj.filepath.save('dummyfile', nf)
>>> dobj=DerivedModel()
>>> dobj.basemodel_ptr=obj
>>> dobj.save()
>>> dobj.id
56
>>> dobj.filepath
<FieldFile: None>
>>> obj.filepath
<FieldFile: y>
Update: for #dgel's answer:
save_base() does it save the derived object? dobj does not get id after that.
After dobj.save(), it seems attributes in base class are getting overwritten by attributes in derived class.
I added ctime created time in BaseModel with default datetime.datetime.utcnow. So once I save derived object, ctime is updated to save time of derived object.
When I look at the DB through sqlitebrowser, filepath field of the BaseModel row is empty.
>>> dobj.save_base(raw=True)
>>> dobj.id
>>> dobj.save()
>>> dobj.filepath
<FieldFile: None>
>>> obj.ctime
datetime.datetime(2012, 8, 23, 8, 50, 3, 171573)
>>> dobj.ctime
datetime.datetime(2012, 8, 23, 8, 51, 9, 946434)
>>> newdobj = DerivedModel.objects.get(id=dobj.id)
>>> newdobj.ctime
datetime.datetime(2012, 8, 23, 8, 51, 9, 946434)
Thanks.
Try this:
dobj = DerivedModel()
dobj.basemodel_ptr=obj
dobj.save_base(raw=True)