Django ForeignKey and associated values in initial model - django

First off, sorry for this somewhat crappy title: I wasn't able to find a short and explicaive title for this one .
I'm currently trying to create an app where there will be players and each will have an inventory.
The inventory consists of a ManyToManyField of Items.
class Item(models.Model):
name = models.CharField(_('Name'), max_length=67)
flavor_text = models.TextField(_('Flavor text'))
class Inventory(models.Model):
items = models.ManyToManyField(Item, verbose_name=_('Items'))
I'm trying to implement a system where a player can, for example, have 3 bananas, 1 screwdriver and 17 loafs in his inventory.
As I'm very lazy, I don't want to enter manually 3 "banana" items in my inventory.
However, putting a quantity field in the Item model would be counter-productive, as I would need to create an Item named "3 bananas", another "2 bananas", again and again.
I also tought of creating Alice's Bananas, Bob's Bananas, and change the quantity, but still, that's creating too much banana items : especially if I need to add more players.
So, here is my question: How can I associate a value (here, a quantity) to a ForeignKey, but inside the model declaring the ForeignKey?
Thanks for reading!

Related

In Django, can I have one model field whose value is the highest value in a one-to-many relationship?

I have two models. One represents several groups, and the other represents people in those groups with a ranking. I would like to have a field in the groups model that represents the highest ranked person inside that group. Is this possible?
For example:
Groups:
id name highest
1 alpha gold
2 bravo diamond
People:
name group rank
Dave 1 silver
bob 1 gold
dilan 1 silver
arthur 2 gold
mark 2 diamond
I would like to have a field in the groups model that represents the highest ranked person inside that group.
In this case, you might want to add a ForeignKey to your Group model. What a foreign key does is it links one of the fields (which you can name it whatever you want) to another model class, which in your case, will be the User model.
But seeing as you want the highest ranked person, all the above actions will deem unrealistic as every time the leaderboard changes you need to refresh your model, and that's inconvenient.
What I'll suggest, instead of adding another field, is to render all the person objects and pass it as a part of the context. That way, as the user's rank changes, you don't need to refresh your model. Please refer to the doc's detail on passing context to view and a way to sort your data.

Can you suggest better Django model design? (I'm django beginner)

I'm designing a simple Django model called Clothes. Basically it retrieves what kinds of clothes the user have. I categorized clothes as more than 20 types (such as hoodies, jean, pants) and as big three types: "top", "bottom", "shoes".
In ClothesView, I want to show first 5 clothes for each "top", "bottom", "shoes". And it will retrieve 5 more for individual category if user clicks more (So if user click 'more top', it will return 5 more clothes of 'top' type.
For your better understanding, I wrote the Clothes model conceptually.
class Clothes(models.Model):
id
type = # hoodie, shirts, pants, jean, coat, and so on (more than 20)
big_type = # top, bottom and shoes
owner = ForeignField # some one who post
Expected output (this is just my guess!)
Retrieve 5 clothes for each parent_types ("top", "bottom", "shoes")
user.clothes_set.filter(big_type="top")[:5]
user.clothes_set.filter(big_type="bottom")[:5]
user.clothes_set.filter(big_type="shoes")[:5]
Retrieved 5 more clothes for "top"
user.clothes_set.filter(big_type="top)[5:10]
Retrieve all "hoodies" from my clothes <- this looks ok
user.clothes_set.filter(type="hoodies")
Can you suggest better of efficient model? I may add new Type class and put "through" ... (I'm not sure)
I think you are doing it right. Read here, limiting-queryset.
If you want to query the database in this way. This is the good option instead of retrieving the whole queryset and then slicing it. Through this, the LIMIT for slicing will be issued to the database so, slicing will be done in the database.
On the second opinion, I would advise you to create a new model for Types with the fields type and id.
Then, map it using ManyToMany Relation, if you are not in the case.
For that querying may look like this,
type = Type.objects.get(type="Top").id
clothes = Clothes.objects.filter(type=type)[:5]
Having a different model for Type will help you at the client side rendering.

Django ManyToMany Query OR Issue

I've got a problem for the following data:
Class Car:
name = ...
year_price_data = ManyToMany(YearPriceData)
Class YearPriceData:
year = Integer
min_price = Integer
max_price Integer
So you can have this car for example:
Ford Focus:
---2014: 25,000$ - 40,000$
---2012: 7000$ - 23,000$
Now, I want to query all cars that match a certain year-price values:
for example: all cars that are sold in 2014 in the price range of 18,000-20,000$
(Should returns nothing in our example, yes?)
so this is the query ive made:
Car.objects.filter(year_price_data__year=2014)
.filter(Q(year_price_data__min_price__lte=20000,year_price_data__min_price__gte=18000) | Q(year_price_data__max_price__lte = 20000,year_price_data__max_price__gte=18000))
The problem that because of the OR it returns the row of 2012, when it supposed to return nothing!
So my question is: how to make an OR query just for every manytomany row, so the query will be exact?
Firstly, I don't see why you want a ManyToMany relationship here. Although a car can have many yearpricedata instances, each yearpricedata only relates to one car. So the proper relationship is a ForeignKey from YearPriceData to Car.
Either way, you should read the documentation on spanning multi-valued relationships. As explained there, in order for the two conditions to be both applied on the same entities, you need to have them in the same filter call. This should work:
Car.objects.filter(year_price_data__year=2014, Q(year_price_data__min_price__lte=20000,year_price_data__min_price__gte=18000) | Q(year_price_data__max_price__lte = 20000,year_price_data__max_price__gte=18000))

Using Django ORM, how do I filter against two fields of the same record in a Many relationship?

I have a DB schema like so:
class Family(models.Model):
pass
class Child(models.Model):
family = models.ForeignKey(Family)
name = models.TextField()
age = models.IntegerField()
gender = models.CharField(max_length=1)
I want a query that returns all children in families that have boys under 5.
How do I express this? Closest I got is:
# WRONG: this is no good, it will only return boys under 5, but I want all
# children in families with boys under 5.
Child.objects.filter(gender='M', age__lt=5)
# WRONG: this is no good, it is closer but will also return children in
# families with a 6yo boy and a 3yo girl.
Child.objects.filter(family__child__gender='M', family__child__age__lt=5)
You stated that you wanted all boys in families with boys under 5
It's a big ambiguous but I'm going to assume that you want families that contain at least one boy that is under 5
This would work:
families = Family.objects.filter(child__gender='M', child__age__lt=5).prefetch_related('child')
Then you could iterate over the family objects to retrieve their respective children
Get the ids of the Familys that have boys under 5, then filter Child on those ids. The following query only hits the database once:
Child.objects.filter(
family__in=Family.objects.filter(child__gender='M', child_age__lt=5).values('id')
)
You can try an __in-query like this:
boys_under_5 = Child.objects.filter(gender='M', age__lg=5)
Child.objects.filter(family__child__in=boys_unter_5)
As to why your original query doesn't work, I suggest reading the generared SQL:
print Child.objects.filter(family__child__gender='M', family__child__age__lt=5).query
As #knbk pointed out in a comment, I was actually wrong when I wrote the question. I assumed a default behavior that wasn't there. The best answer, then, is the one I gave:
Child.objects.filter(family__child__gender='M', family__child__age__lt=5)
If you wanted to retrieve all children in families with a male child and one (possibly a different one) who is under 5, you'd have to do this:
Child.objects.filter(family__child__gender='M').filter(family__child__age__lt=5)
And here's the documentation link #knbk provided which details the feature: https://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships
If he answers, I'll move the points to him.

How to model lending items between a group of companies

I have a group of related companies that share items they own with one-another. Each item has a company that owns it and a company that has possession of it. Obviously, the company that owns the item can also have possession of it. Also, companies sometimes permanently transfer ownership of items instead of just lending it, so I have to allow for that as well.
I'm trying to decide how to model ownership and possession of the items. I have a Company table and an Item table.
Here are the options as I see them:
Inventory table with entries for each Item - Company relationship. Has a company field pointing to a Company and has Boolean fields is_owner and has_possession.
Inventory table with entries for each Item. Has an owner_company field and a possessing_company field that each point to a Company.
Two separate tables: ItemOwner and ItemHolder**.
So far I'm leaning towards option three, but the tables are so similar it feels like duplication. Option two would have only one row per item (cleaner than option one in this regard), but having two fields on one table that both reference the Company table doesn't smell right (and it's messy to draw in an ER diagram!).
Database design is not my specialty (I've mostly used non-relational databases), so I don't know what the best practice would be in this situation. Additionally, I'm brand new to Python and Django, so there might be an obvious idiom or pattern I'm missing out on.
What is the best way to model this without Company and Item being polluted by knowledge of ownership and possession? Or am I missing the point by wanting to keep my models so segregated? What is the Pythonic way?
Update
I've realized I'm focusing too much on database design. Would it be wise to just write good OO code and let Django's ORM do it's thing?
Is there a reason why you don't want your item to contain the relationship information? It feels like the owner and possessor are attributes of the item.
class Company(models.Model):
pass
class Item(models.Model):
...
owner = models.ForeignKey(Company, related_name='owned_items')
holder = models.ForeignKey(Company, related_name='held_items')
Some examples:
company_a = Company.objects.get(pk=1)
company_a.owned_items.all()
company_a.held_items.all()
items_owned_and_held_by_a=Items.objects.filter(owner=company_a, holder=company_a)
items_on_loan_by_a=Items.objects.filter(owner=company_a).exclude(holder=company_a)
#or
items_on_loan_by_a=company_a.owned_items.exclude(holder=company_a)
items_a_is_borrowing=Items.objects.exclude(owner=company_a).filter(holder=company_a)
#or
items_a_is_borrowing=company_a.held_items.exclude(owner=company_a)
company_b = Company.objects.get(pk=2)
items_owned_by_a_held_by_b=Items.objects.filter(owner=company_a, holder=company_b)
#or
items_owned_by_a_held_by_b=company_a.owned_items.filter(holder=company_b)
#or
items_owned_by_a_held_by_b=company_b.held_items.filter(owner=company_a)
I think if your items are only owned by a single company and held by a single company, a separate table shouldn't be needed. If the items can have multiple ownership or multiple holders, a m2m table through an inventory table would make more sense.
class Inventory(models.Model):
REL = (('O','Owns'),('P','Possesses'))
item = models.ForeignKey(Item)
company = models.ForeignKey(Company)
relation = models.CharField(max_length=1,choices=REL)
Could be one implementation, instead of using booleans. So I'd go for the first. This could even serve as an intermediate table if you ever decide to use a 'through' to relate items to company like this:
Company:
items = models.ManyToManyField(Item, through=Inventory)
Option #1 is probably the cleanest choice. An Item has only one owner company and is possessed by only one possessing company.
Put two FK to Company in Item, and remember to explicitly define the related_name of the two inverses to be different each other.
As you want to avoid touching the Item model, either add the FKs from outside, like in field.contribute_to_class(), or put a new model with a one-to-one rel to Item, plus the foreign keys.
The second method is easier to implement but the first will be more natural to use once implemented.