In Django, what does symmetrical=True do? - django

For example:
class Contact(models.Model):
contacts = models.ManyToManyField('self', through='ContactRelationship', symmetrical=False)
What does the symmetrical=False parameter do?
When should it be left as True, and when should it be set as False?
How does this settings affect the database (does it create extra columns etc)?

Let's say you have two instances of Contact, John and Judy. You may decide to make John a contact of Judy. Should this action also make Judy a contact of John? If so, symmetrical=True. If not, symmetrical=False

Here is what is says in the documentation:
Only used in the definition of ManyToManyFields on self. Consider the following model:
from django.db import models
class Person(models.Model):
friends = models.ManyToManyField("self")
When Django processes this model, it identifies that it has a ManyToManyField on itself, and as a result, it doesn’t add a person_set attribute to the Person class. Instead, the ManyToManyField is assumed to be symmetrical – that is, if I am your friend, then you are my friend.
By default, the value of symmetrical is True for Many to Many Field which is a bi-directional relationship.
Using a through table (symmetrical=False):
But you can also imagine a situation where you don't need this type of relationship so you can add symmetrical=False. And, this can be achieved by using a through table because by default symmetrical is False if you use a through table:
Recursive relationships using an intermediary model are always defined as non-symmetrical – that is, with symmetrical=False – therefore, there is the concept of a “source” and a “target”. In that case 'field1' will be treated as the “source” of the relationship and 'field2' as the “target”.
So you can imagine a situation where you do need the direction i.e. let's say there is a Node model and it has a relationship with itself using a through table. If we didn't have the requirement of direction here we could go with the example shown earlier. But now we also need a direction from one node to another where one being source and another one being target and due to nature of this relationship it cannot be symmetrical.

Related

Best approach to model contacts in the Django modelling language

I am designing a contact relationship application that needs to store contacts in groups. Basically I have 7 "group types" (simplified it to 3 for my image), each group type shares the same fields so I thought that it would make sense to use an abstract "group", and let all group types inherit the methods from this abstract group.
So this is basically the idea:
However, this approach results in a couple of unexpected difficulties. For example:
I am not able to use a foreignkey of an abstract class, so if I would want to model a relationship between a group and a contact, I have to use the following approach:
limit = (models.Q(app_label='groups', model="Group type A") |
models.Q(app_label='groups', model="Group type B") |
models.Q(app_label='groups', model="Group type C")
)
group_type = models.ForeignKey(ContentType, limit_choices_to=limit)
group_id = models.PositiveIntegerField()
group = GenericForeignKey('group_type', 'group_id')
This seems quite hacky, and with this approach I am forced to do some hard coding as well. I am not able to call all groups with a simple query, maybe a new group will be added in the future.
Is there a better approach to model a relationship like this? Am I using the abstract class completely wrong?
Edit: some extra explanation in response to the questions.
A user is connected to a group with another object called "WorkRelation", because there is some extra data that is relevant when assigning a user to a group (for example his function).
I initially went for an abstract class because I thought that this would give me the flexibility to get all Group types be just calling Group.objects.all(). If I would use a base model, the groups aren't connected and I will also have to hard-code all group names.
Since your child models do not have additional fields, you can make them proxy models of the base group model. Proxy models do not create new database tables, they just allow having different programmatic interfaces over the same table.
You could then define your ForeignKey to the base group model:
group = ForeignKey(BaseGroup)
Use django-polymodels or a similar app to have the groups casted to the right type when queried.
More on model inheritance in the doc.
Why don't use solid base model instead of abstract model? Then you just put contacts as either ForeignKey or ManyToMany to the base model.

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.

How do I create multiple One-to-Many relationships to the same table in Django?

So first off, I want to clarify that I am trying to make One-To-Many relationships, not Many-to-One. I already understand how ForeignKeys work.
For the sake of the discussion, I've simplified my models; they're much more field-rich than this in the real implementation.
I have a model, called a ColumnDefinition:
class ColumnDefinition(Model):
column_name = CharField(max_length=32)
column_type = PositiveSmallIntegerField()
column_size = PositiveSmallIntegerField(null=True, blank=True)
I think have a registry. Each registry has a separate set of columns for it's input and output definition. I've put the theoretical "OneToManyField" in there to demonstrate what I'm trying to do.
class Registry(Model):
input_dictionary = OneToManyField(ColumnDefinition)
output_dictionary = OneToManyField(ColumnDefinition)
created_date = DateTimeField(auto_now_add=True, editable=False)
A ColumnDefinition is only ever related to one Registry ever. So it's not a Many to Many relationship. If I put a ForeignKey on the ColumnDefinition instead to create a reverse relationship, it can only create a single reverse, whereas I need both an input and output reverse.
I don't want to have to do anything kludgey like adding a "column_registry_type" field onto ColumnDefinition if I can get around it.
Does anyone have any good ideas on how to solve this problem?
Thanks!
You can add two ForeignKeys on ColumnDefinition, one for input and one for output, and give them separate related_names:
class ColumnDefinition(Model):
...
input_registry = models.ForeignKey(Registry, related_name='input_columns')
output_registry = models.ForeignKey(Registry, related_name='output_columns')
You can then access the set of columns like registry.input_columns.
You can and should define two different ForeignKey fields on ColumnDefinition. Just make sure to specify a related_name value for at least one of them.
https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.related_name

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.

Does order of declaration matter in models.py (Django / Python)?

I have something like this in models.py
class ZipCode(models.Model):
zip = models.CharField(max_length=20)
cities = City.objects.filter(zip=self).distinct()
class City(models.Model):
name = models.CharField(max_length=50)
slug = models.CharField(max_length=50)
state = models.ForeignKey(State)
zip = models.ManyToManyField(ZipCode)
When I do this I get:
NameError: name 'City' is not defined
Is this because the order of declaration matters? And if so, how can I do this, because either way I arrange this, it looks like I'm going to get a NameError.
Thanks.
Apart from order issues, this is wrong:
cities = City.objects.filter(zip=self).distinct()
It is not inside a method, so "self" will also be undefined. It is executed only once, at class-creation time (i.e. when the module is first imported), so the attribute created would be a class attribute and have the same value for all instances. What you might be looking for is this:
#property
def cities(self):
return City.objects.filter(zip=self).distinct()
Because this is inside a method, which is not executed until it's accessed, ordering issues will no longer be a problem. As ozan points out, this is a duplication of what Django reverse relations already give you for free:
a_zip_code.city_set.all()
And you can use related_name to call it what you like:
zip = models.ManyToManyField(ZipCode, related_name='cities')
...
a_zip_code.cities.all()
So I don't think the ordering issue you originally asked about is even relevant to your situation. When it is, others have already pointed out using quoted strings in ForeignKey and ManyToManyField declarations to get around it.
When you have references to classes defined after, you can use this trick:
attribute = models.ForeignKey('ClassDefinedAfterThis')
Yes order does matter as others have noted.
Though, encountering this issue is almost always going to be an indication that you're doing something wrong.
In this case your declaration:
cities = City.objects.filter(zip=self).distinct()
... is both redundant and bad practice. You can find the cities related to a zip code by referring to that zip code's city_set in your views (ie not in your model!). So if zip is an instance of ZipCode, you would do:
cities = zip.city_set.all()
If you really want to call it 'cities' rather than 'city_set' you can use the related_name parameter in your m2m declaration.
I was once worried about order... because I thought my models below could only reference models above. But then realized that you can just do a
models.ForeignKey('appName.modelName')
and all was fine.
Yes, order does matter, but your example does not look right to me. I think you should just be using a foreign key for your many-to-one relationship:
cities = models.ForeignKey(City)
This has the details on many-to-one relationships with django models.
Edit:
It was pointed out to me in the comments that cities in Europe might have several cities in the same zip code. If you are looking for a many-to-many relationship here, you should use:
cities = models.ManyToManyField(City)
This is described in Django's documentation. The point is, this is either of these examples are much more clear than what is used in the example.
Order matters in Python. This thread may be relevant to your question. Also for your uses, you may want to use a unique foreign key in the ZIP code class.