Django model structure - best practice - django

For a little free fruit site, I have the following model relationship:
3 classes - Apples, Strawberries, and Locations
Apples and Strawberries list all "properties" of the fruits:
class Apple(models.Model):
treeSize = ...
age = ...
class Strawberry(models.Model):
fieldArea = ...
Now I want to record all locations with the fruits. So far I have a class with locations and attributes for Apples and Strawberries, which keeps the ForeignKeys to the kind of fruits. But that can't be it. It looks very inefficient, esp. if I add multiple other fruit types. Also, I would need to check that each location has at least one of the fruit keys.
class Location(models.Model):
lat = ...
lng = ...
appleType = models.ForeignKey(Apple, null = True)
strawberryType = models.ForeignKey(Strawberry, null = True)
Is there a better way to design the relationships or the model structure?
What would be the best practice for this problem?
Thank you for your advice!

This is a perfect case where you need to use the Generic Relations.
Instead of storing the Foreign Key to each one of those, you store the content type id (that recognizes Strawberries from Apples) and the object ids. You can of-course, develop this with your own conventions using the relevant strings within the Location model, but all that hard work is done for you in the django's built in Content Type Framework.

Related

How to make an union of prefetch_related from different foreign keys?

I have three models, let's take an imaginary example:
class Entity(models.Model):
name = models.CharField()
class EntityAssociation(models.Model):
buddy1 = models.ForeignKey(Entity, related_name='+')
buddy2 = models.ForeignKey(Entity, related_name='+')
class EntityPhoto(models.Model):
entity = models.ForeignKey(Entity, null=True)
association = models.ForeignKey(EntityAssociation, null=True)
title = ...
We have some people (Entity), that can share personal photos of themselves. We also have some relations between entities (represented by EntityAssociation) that can also share photos of them together.
For a single entity, I can retrieve all the photo associated to an entity, either directly or through an association, doing so:
obj = Entity.objects.last()
EntityPhoto.objects.filter(
Q(entity=obj) | Q(association__buddy1=obj) | Q(association__buddy2=obj)
)
What I want is being able to prefetch all the photos of a set of entities selected. A typical use-case would be:
for entity in Entity.objects.all().prefetch(???):
print(entity.name, 'has', len(entity.photos_prefetched), 'photos')
print([x.title for x in entity.photos_prefetched])
And this should be returning all the photos. A solution with three queries (Entity listing, prefetch through entity, prefetch through association attr ; two would be perfect) would satisfy me but the more important is to be able to iterate through a single list, on each entity
I tried to look the internal code of Prefetch but it looks like a prefetch is tied to a lookup, plus I don't know how to make the Q query in this case (what should be the right operand in Q(entity__in=...)?)
Notice: The point here is not about refactoring the database structure (EntityAssociation is used for a plenty of other things so it can't be reduced to a M2M of EntityPhoto for example.) but optimizing this specific use-case, if possible.
I'm currently playing a bit with Prefetch. In my opinion it's in a pretty good shape and quite powerful.
My approach to your problem would probably be something like:
entities = Entity.objects.all().prefetch(Prefetch(
'entity_photo__set',
EntityPhoto.objects.filter(
Q(entity=obj) | Q(association__buddy1=obj) | Q(association__buddy2=obj
)
to_attr="photos_prefetched",
))
You can then access those photos with for entity in entities: entity.photos_prefetched.
If this is not working, the problem might be that you're referencing back (Q(entity=obj)) to the actual entity. Not sure if this is properly possible. I'm having trouble with references back to the object, might be a bug in Django.

Deciding how to model this data in django

Imagine it is for translating vocabulary to another language. I'm only dealing with a limited number of words (~2000).
Language1 and Language2 (which will have a different ~2000 words), each might have multiple words equivalents from the other language which may or may not be on the list of ~2000 words of the other language.
Using a many-to-many relationship initially appealed to me, but I can't quite see through the mist to see what would work best.
My other thought was just making a json dump for each word. Something like....
{1: {'Lang1': [word1, word2], 'Lang2': [word1, word2]}}
but I am not sure if that is too smart to manage everything like that, it would be cumbersome to do from the admin section (because I think I would be editing a long line of text that is a json object) and it doesn't take advantage of much.
Maybe there is another way that I havent thought of?
Given my scenario, how would you go about defining this?
class Language(models.Model):
name = models.CharField(max_length=100)
class Word(models.Model):
name = models.CharField(max_length=100)
language = models.ForeignKey(Language, related_name='language_words')
#...
class Translation(models.Model):
word = models.ForeignKey(Word, related_name='word_translations')
translation = models.CharField(max_length=100)
from_language = models.ForeignKey(Language, related_name='language_translations')
in_language = models.CharField(max_length=100)
# stage performances
english_language = Language(name='english')
english_language.save()
word = english_language.language_words.create(name='Flower')
german_translation = word.word_translations.create(translation='Blumen',
from_language=english_language,
in_language='German')
word # 'Flower'
german_translation # 'Blumen'
might not be optimal yet, i am in the train right now, but this can be a good way to start hopefully.
then if you register these models into admin, you can easily manage (add/delete) translations..

Django INNER JOIN by field

Say I have a model that is
class Bottles(models.Model)
BottleCode = models.IntegerField()
class Labels(models.Model)
LabelCode = models.IntegerField()
How do I get a queryset of Bottles where the BottleCode and LabelCode are equal? (i.e. Bottles and Labels with no common Code are excluded)
It can be achieved via extra():
Bottles.objects.extra(where=["Bottles.BottleCode in (select Labels.LabelCode from Labels)"])
You may also need to add an app name prefix to the table names, e.g. app_bottles instead of bottles.
Though #danihp has a point here, if you would often encounter queries like these, when you are trying to relate unrelated models - you should probably think about changing your model design.

Clean approach to store Key-Value "properties"?

Let say I have one model for cars. Each car has attributes: color, size, weight, etc... I want users to allow the creation of new attributes for each car objects. However I want to share "attributes" so that the attribute "size" only exists once in the db but values exist for each object where users have added them.
My old approach were two models: Car, KeyValue(key: String, value:String) and Car had a 1:m relationship to KeyValue.
Now, to ensure my above constraint I thought of the following:
Three objects: Car, Key, Value
Value(Key: Key)
Car (1:m relation to Value)
However, this reverse approach works very well but does not look "clean" to me. Therefore, I'd like to ask whether a cleaner approach is possbile.
i read this
http://www.craigkerstiens.com/2012/06/11/schemaless-django/
i haven't used this but i think it's ok.
a less complex approach is
django-picklefield
http://pypi.python.org/pypi/django-picklefield/
i hope this can help you
If your "choices" are malleable, i.e. they can be added to, edited, or deleted from, then the choice should be it's own model. For example:
class Color(models.Model):
name = models.CharField(max_length=50)
class Car(models.Model):
color = models.ForeignKey(Color, related_name='cars')
You should have Cars and CarModels
A CarModel has two list of properties. One that has the "PerModel" attributes for all cars of that models (That means all the cars of that model have the same value), and another one (just the names) of the properties that each car can have a different value "PerCar".
CarModel
{
KeyValue(key: String, value:String) propertyValuesPerModel;
List(String) propertyNamesPerCar;
}
Car
{
CarModel model;
KeyValue(key: String, value:String) propertiesPerCar; //Keys can only be those that appear on model.propertyNamesPerCar
}

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.