Combine one-to-one and many-to-many (django) - django

I have a model named "db_connector" and another one "project_site".
A "project_site" can have many to many relation with "db-connector".
But the "project_site" should have one default db-connector.
What is the best design-pattern for this?
Can I combine many to many and one to one like this:
Many to many relation for alternative connectors linked to a project_site and one to one for the default connector linked to a project_site?
The user can specify a db_connector, if the user doesn't specify a database_conector, the default db_connector can be used?
Using this pattern on django causes an error (fields.E305)
Project_site.alternative_db_connectors: (fields.E305) Reverse query name for 'Project_site.alternative_db_connectors' clashes with reverse query name for 'Project_site.default_db_connector'.
HINT: Add or change a related_name argument to the definition for 'Project_site.alternative_db_connectors' or 'Project_site.default_db_connector'.
The project_site models looks like this:
class Project_site(models.Model):
name = models.CharField(max_length=150)
project_group = models.ForeignKey(Project_Group, on_delete=models.CASCADE)
default_db_connector = models.OneToOneField(DB_Connector, on_delete=models.CASCADE , primary_key=True ,)
alternative_db_connectors = models.ManyToManyField(DB_Connector)
def __str__(self) -> str:
return self.name
In SQL terms it should be possible, right?

Related

How to get objects which have an object in their ManyToManyField?

There's this model:
class User(AbstractUser):
followers = models.ManyToManyField('self', symmetrical=False, related_name='followings', null=True, blank=True)
Say I have a user object called 'A'. In a view, I want to filter User objects which have 'A' as their follower. How can I do that?
You can query with:
A.followings.all()
The related_name=… [Django-doc] is the name of the relation in reverse, so it is a QuerySet of Users that have A as follower.
If you do not specify a related_name=… it will take the name of the model in lowercase followed by the …_set suffix, so user_set in this case.
If you only have the primary key of A, then you can query with:
User.objects.filter(followers__id=a_id)
Note: Using null=True [Django-doc] for a ManyToManyField [Django-doc] makes no sense: one can not enforce by the database that a ManyToManyField should be non-empty, therefore as the documentation says: "null has no effect since there is no way to require a relationship at the database level."

What does related_name do?

In the Django documentation about related_name it says the following:
The name to use for the relation from the related object back to this one. It’s also the default value for related_query_name (the name to use for the reverse filter name from the target model). See the related objects documentation for a full explanation and example. Note that you must set this value when defining relations on abstract models; and when you do so some special syntax is available.
If you’d prefer Django not to create a backwards relation, set related_name to '+' or end it with '+'.
I didn't understand it clearly. If somebody would please explain it a bit more, it would help me a lot.
When you create a foreign key, you are linking two models together. The model with the ForeignKey() field uses the field name to look up the other model. It also implicitly adds a member to the linked model referring back to this one.
class Post(models.Model):
# ... fields ...
class Comment(models.Model):
# ... fields ...
post = models.ForeignKey(Post, related_name=???)
There are three possible scenarios here:
1. Don't specify related_name
If you don't specify a name, django will create one by default for you.
some_post = Post.objects.get(id=12345)
comments = some_post.comment_set.all()
The default name is the relation's name + _set.
2. Specify a custom value
Usually you want to specify something to make it more natural. For example, related_name="comments".
some_post = Post.objects.get(id=12345)
comments = some_post.comments.all()
3. Prevent the reverse reference from being created
Sometimes you don't want to add the reference to the foreign model, so use related_name="+" to not create it.
some_post = Post.objects.get(id=12345)
comments = some_post.comment_set.all() # <-- error, no way to access directly
related_query_name is basically the same idea, but when using filter() on a queryset:
posts_by_user = Post.objects.filter(comments__user__id=123)
But to be honest I've never used this since the related_name value is used by default.
If in a model you have a ForeignKey field (this means you point through this field to other model):
class Author(models.Model):
name = ......
email = .....
class Article(models.Model):
author = models.ForeignKey(Author)
title= ....
body = ....
if you specify related_name on this field
class Article(modles.Model):
author = models.ForeignKey(Author, related_name='articles')
you give a name to the attribute that you can use for the relation (named reverse realationship) from the related object back to this one (from Author to Article). After defining this you can retrieve the articles of an user like so:
author.articles.all()
If you don't define a related_name attribute, Django will use the lowercase name of the model followed by _set (that is, in our case, article_set) to name the relationship from the related object back to this one, so you would have to retrieve all articles of an user like so:
author.article_set.all()
If you don't want to be possible a reverse relationship (from the model to which points your ForeignKey filed to this model (the model in which the ForeignKey field is defined) you can set
class Author(models.Model):
author = models.ForeignKey(User, related_name='+')

How to set related_name with a ManyToManyField using a through table and pointing to self

I have a task class, that can have sub-tasks, so it's a cyclic relationship. I'm passing it through a linker model/table like so:
class Task(models.Model):
title = models.CharField(max_length=100)
description = models.TextField(blank=True)
completed = models.BooleanField(default=False)
project = models.ForeignKey('Project', related_name="tasks")
dependancy = models.ManyToManyField('self', through='Dependancy', null=True,
blank=True, through_fields=('task', 'sub_task'), symmetrical=False)
def __str__(self):
return self.title
class Dependancy(models.Model):
task = models.ForeignKey(Task)
sub_task = models.ForeignKey(Task)
But I get this error:
ERRORS:
gantt_charts.dependency.sub_task: (fields.E303) Reverse query name for 'dependency.sub_task' clashes with field name 'Task.dependency'.
HINT: Rename field 'Task.dependency', or add/change a related_name argument to the definition for field 'dependency.sub_task'.
gantt_charts.dependency.sub_task: (fields.E304) Reverse accessor for 'dependency.sub_task' clashes with reverse accessor for 'dependency.task'.
HINT: Add or change a related_name argument to the definition for 'dependency.sub_task' or 'dependency.task'.
gantt_charts.dependency.task: (fields.E303) Reverse query name for 'dependency.task' clashes with field name 'Task.dependency'.
HINT: Rename field 'Task.dependency', or add/change a related_name argument to the definition for field 'dependency.task'.
gantt_charts.dependency.task: (fields.E304) Reverse accessor for 'dependency.task' clashes with reverse accessor for 'dependency.sub_task'.
HINT: Add or change a related_name argument to the definition for 'dependency.task' or 'dependency.sub_task'.
Obviously I need to set the related name on the Dependency.sub_task and Dependency.task field, and following the solution here is to name them something like task_task and task_sub_task, but that sounds wrong, unintuitive and confusing.
What would be a clear and concise name for them? It'd be easier if I wasn't getting confused over what related_names were, when using a linking table.
Given a Task instance, how can you access all the Dependencies that have that as their task? Or their sub_task? That's the purpose of related_name—it provides the name for the attribute that Django will create on Task to point to that group of things.
You're seeing that error because Django automatically uses the name <model>_set, and since you have two ForeignKeys pointing to the same model, the default name will conflict.
Now, it might be that you never need to directly access Dependencies that way. If that's the case you can add related_name='+' to both fields and the reverse attribute won't be created at all (and your errors will disappear).
If you do want to access them, the name is up to you. I prefer longer-but-more-descriptive names to make their purpose clear. I might model the problem like this:
class Task(models.Model):
subtasks = models.ManyToManyField('self',
through='Dependancy',
symmetrical=False,
through_fields=('supertask', 'subtask'),
related_name='supertasks')
class Dependancy(models.Model):
supertask = models.ForeignKey(Task, related_name='dependencies_as_supertask')
subtask = models.ForeignKey(Task, related_name='dependencies_as_subtask')
class Meta:
unique_together = ('supertask', 'subtask')
>>> task = Task.objects.get()
>>> # all the Tasks that are supertasks of this one
>>> task.supertasks
>>> # all the Tasks that are subtasks of this one
>>> task.subtasks
>>> # all the Dependencies with this Task as the supertask
>>> task.dependencies_as_supertask
>>> # all the Dependencies with this Task as the subtask
>>> task.dependencies_as_subtask

Should I use a seperate table instead of many to many field in Django

I needed to assign one or more categories to a list of submissions, I initially used a table with two foreign keys to accomplish this until I realized Django has a many-to-many field, however following the documentation I haven't been able to duplicate what I did with original table.
My question is : Is there a benefit to using many-to-many field instead of manually creating a relationship table? If better, are there any example on submitting and retrieving many-to-many fields with Django?
From the Django docs on Many-to-Many relationships:
When you're only dealing with simple many-to-many relationships such
as mixing and matching pizzas and toppings, a standard ManyToManyField
is all you need. However, sometimes you may need to associate data
with the relationship between two models.
In short: If you have a simple relationship a Many-To_Many field is better (creates and manages the extra table for you). If you need multiple extra details then create your own model with foreign keys. So it really depends on the situation.
Update :- Examples as requested:
From the docs:
class Person(models.Model):
name = models.CharField(max_length=128)
def __unicode__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __unicode__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
You can see through this example that membership details (date_joined and invite_reason) are kept in addition to the many-to-many relationship.
However on a simplified example from the docs:
class Topping(models.Model):
ingredient = models.CharField(max_length=128)
class Pizza(models.Model):
name = models.CharField(max_length=128)
toppings = models.ManyToManyField(Topping)
There seems no need for any extra data and hence no extra model.
Update 2 :-
An example of how to remove the relationship.
In the first example i gave you have this extra model Membership you just delete the relationship and its details like a normal model.
for membership in Membership.objects.filter(person__pk=1)
membership.delete()
Viola! easy as pie.
For the second example you need to use .remove() (or .clear() to remove all):
apple = Toppings.objects.get(pk=4)
super_pizza = Pizza.objects.get(pk=12)
super_pizza.toppings.remove(apple)
super_pizza.save()
And that one is done too!

Django: related_name attribute (DatabaseError)

i have this problem with my models.
class Message(models.Model):
user = models.ForeignKey(UserProfile)
text = models.TextField(max_length=160)
voting_users = models.ManyToManyField(UserProfile)
def __unicode__(self):
return self.text
and
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
def __unicode__(self):
return self.user.username
I get this error when i try to call message.voting_users:
message: Accessor for m2m field 'voting_users' clashes with related field
'UserProfile.message_set'. Add a related_name argument to the definition for
'voting_users'.
I'm actually new to django and i don't get it how i should use related_name attribute.
As it says, voting_users, needs a related_name argument because it clashes with an already defined related field, message_set (an automagic property created by django for your first ForeignKey, Message.user)
http://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.related_name
You must supply a related name argument to either of your ForeignKey / m2m fields to define a unique accessor for the reverse relationship.
For example, the reverse relationship for the Message model on UserProfile is UserProfile.message_set. If you have two ForeignKey's you're attempting to create two different reverse relationships with the same UserProfile.message_set method.
user = models.ForeignKey(UserProfile, related_name="message_user")
...
# would create a UserProfile.message_user manager.
userprofile.message_user.all() # would pull all message the user has created.
userprofile.message_set.all() # would pull all Messages that the user has voted on.
The problem is that both voting_users and message_set have the same attribute name user. related_name allows you to define an alias for one of the attributes that you can use to avoid name conflicts.
(Edit: Wrong link)
http://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.related_name
This question is very similar to another question listed here:
Django: Why do some model fields clash with each other?