Django model with Foreign Key and ManyToMany relations to same model - django

I have a django model as follows:
class Subscription(models.Model):
Transaction = models.ManyToManyField(Transaction, blank=True, null=True)
User = models.ForeignKey(User)
...etc...
I am trying to add a ManyToMany field to the User model as follows:
SubUsers = models.ManyToManyField(User, blank=True, null=True)
but I get this error when I run syncdb:
AssertionError: ManyToManyField(<django.db.models.fields.related.ForeignKey object at 0x19ddfd0>) is invalid. First parameter to ManyToManyField must be either a model, a model name, or the string 'self'
If I encase User in quotes, I get instead:
sales.subscription: 'User' has a relation with model User, which has either not been installed or is abstract.
I know the User model is imported correctly. Any ideas why having 2 fields pointing to the User model causes problems? Thanks in advance...

The reason why it fails is because the name of your field is the same as the class name (User). Use lowercase field names, it the standard convention in Django and Python. See Django Coding style
Also, you need to add a related_nameparameter to your relationship:
class Subscription(models.Model):
user = models.ForeignKey(User)
sub_users = models.ManyToManyField(User, blank=True, null=True, related_name="subscriptions")

Related

Filtering one model based on another model's field

I have the following models:
class User(AbstractUser):
pass
class Post(models.Model):
post_user = models.ForeignKey(User, on_delete=models.CASCADE)
post_content = models.TextField()
post_date = models.DateTimeField(default=timezone.now)
class Follow(models.Model):
follow_user = models.ForeignKey(User, on_delete=models.CASCADE)
follow_target = models.ForeignKey(User, on_delete=models.CASCADE, related_name='follow_target')
I'm trying to create a queryset of all posts that were created by users I follow (me being the currently logged in user). My queryset currently looks like this but I keep getting NameError: field not found:
postresult = Post.objects.all().filter(post_user=follow__follow_target, follow__follow_user=request.user)
Any help will be greatly appreciated!
You filter with:
postresult = Post.objects.filter(
post_user__follow_target__follow_user=request.user
)
The post_user will follow the ForeignKey from Post to User. Then we use the related_name='follow_target' to follow the relation to the Follow object, and then we use follow_user to retrieve the following user.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Note: The related_name=… parameter [Django-doc]
is the name of the relation in reverse, so from the User model to the Follow
model in this case. Therefore it (often) makes not much sense to name it the
same as the forward relation. You thus might want to consider renaming the follow_target relation to followers.
In case you rename the related name, the query is thus:
postresult = Post.objects.filter(
post_user__followers__follow_user=request.user
)

How to change the primary key of manytomany table in django

I am changing the primary key of the legacy database. I was able to change the primary key by setting id as the primary key.
Before
class User(models.Model):
name = models.CharField(max_length=5)
email = models.CharField(max_length=5)
age = models.CharField(max_length=5)
After
class User(models.Model):
id = models.BigIntegerField(primary_key=True)
name = models.CharField(max_length=5)
email = models.CharField(max_length=5)
age = models.CharField(max_length=5)
Then
python manage.py makemigrations
python manage.py migrate
This is working fine.
But I also want to change the default primary key of the tables created via ManyToMany feild.
User Model
class User(models.Model):
id = models.BigIntegerField(primary_key=True)
name = models.CharField(max_length=5)
email = models.CharField(max_length=5)
age = models.CharField(max_length=5)
UserProfile Model
class UserProfile(models.Model):
id = models.BigIntegerField(primary_key=True)
address = models.CharField(max_length=5)
father_name = models.CharField(max_length=5)
pincode = models.CharField(max_length=5)
user = models.ManyToManyField(User)
The ManytoMany field creates table called User_user_userprofile with id as Autofield basically previous or default django primary key.
id, user_id, userprofile_id
ManytoMany Table
Now, How to change the primarykey of ManytoMany Feild ie id created by Django?
PS:
Django: 1.11
Python: 2.7.5
DB: Sqlite3 3.7.17 2013-05-20
I stumbled upon this problem today, and ended up solving it by using the through argument of the ManyToManyField. I solved it for Django v3.2.6 however, but the documentation for v1.11 mentions the same behavior for the same argument, so hopefully the solution should work for your version of Django too. Here's the link to the documentation for v1.11 ManyToManyField.through
What the through argument allows you to do is to create the intermediary table (created automatically by ManyToManyField) yourself. You get finer control of how the intermediary table should look like, what fields it should have and what their behavior should be. Hope you are getting a picture.
Let me give you the example of the problem I faced and how I solved it. Hopefully that will make this clearer.
I was trying to establish a many-to-many relationship between two of my existing models.
My first model looks like this,
class BanglaWords(models.Model):
class Meta:
verbose_name_plural = 'Bangla Words'
bng_id = models.CharField(max_length=16, primary_key=True)
bangla_word = models.CharField(max_length=64)
def __str__(self):
return self.bangla_word
and the second one looks like,
class EnglishWords(models.Model):
class Meta:
verbose_name_plural = 'English Words'
eng_id = models.IntegerField(primary_key=True)
word = models.CharField(max_length=64)
bangla_word = models.ManyToManyField(BanglaWords)
def __str__(self):
return self.word
But this resulted in an intermediary table wordnet_englishwords_bangla_word which looked like this,
wordnet_englishwords_bangla_word
id
englishwords_id
banglawords_id
But I didn't want this, I wanted bng_id to be the pk for this table. I solved the problem with ManyToManyField.through as follows,
I defined the intermediary model(table) myself and with the through argument, I pointed to the new intermediary model I created and instructed django to create the table the way I wanted it.
First I created the intermediary model,
class BanglaEnglishRelations(models.Model):
class Meta:
verbose_name_plural = 'Bangla English Relations'
bng_id = models.OneToOneField('BanglaWords', primary_key=True, on_delete=models.CASCADE)
eng_id = models.ForeignKey('EnglishWords', on_delete=models.CASCADE)
which defines bng_id as the primary key as I desired.
Second, I told the ManyToManyField in EnglishWords to base the table on BanglaEnglishRelations like,
bangla_word = models.ManyToManyField(BanglaWords, through=BanglaEnglishRelations)
This resulted in the table wordnet_banglaenglishrelations which looked like,
wordnet_banglaenglishrelations
bng_id_id
eng_id_id
and surved my purposes. You can do something similar to solve your problem and promote whatever field to a pk.

How to automatically create an object for a model in Django, if it doesn't exist?

I have two models. One is for UserProfile and the other is for Company.
class UserProfile(models.Model):
company_name = models.ForeignKey(Company, on_delete = models.CASCADE, related_name = 'company')
class Company(models.Model):
name = models.CharField(max_length = 100)
I am using Django Rest Framework for user creation. What I want to achieve is when I create a new user, I want to assign a company_name to that user. And if that company_name is not present in the db, then I want to create it on the go. But it is throwing an error. "Invalid hyperlink - No URL match."
You can use python's #property to tackle this problem in a clean and simple way. It works well for creating and for updating the object aswell. Note that the UserPorifle's field is renamed to company. Here is how you do it:
class Company(models.Model):
name = models.CharField(max_length=100)
class UserProfile(models.Model):
company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name='company')
#property
def company_name(self):
return self.company.name
#company_name.setter
def company_name(self, value):
self.company, _ = Company.objects.get_or_create(name=value)
Now you can create objects using:
UserProfile.objects.create(company_name='Some name')
First you need to link your UserProfile Model with the user. It should be a OnetoOne Relationship because a User should only have one company I guess.
In your serializer you should add in the Company model and save the company name from the input in the API and then connect it to the new user that is being created.

How To? django model field whose value is a choice of a manytomany field of the same model

I have a django model called company with a manytomany field where company members are added.
I have another field, called 'company_contact' where I want to be able to choose from one of the company_members as if it was a ForeingKey to company_members.
Is there an easy way of doing this without customized forms, ajax request, django-autocomplete-light, etc?
I intend to fill this model using django admin.
Thanks
class Dm_Company(models.Model):
company_name = models.CharField(max_length=80, blank=True, verbose_name="Razon Social")
company_members = models.ManyToManyField(conf_settings.AUTH_USER_MODEL, verbose_name="Miembros")
#company_contact = models.ForeignKey(conf_settings.AUTH_USER_MODEL, related_name="company_members", on_delete=models.CASCADE)
company_phone = models.CharField(max_length=80, blank=True, verbose_name="Telefono compania")
company_email = models.CharField(max_length=80, blank=True, verbose_name="Email compania")
The one way that I can think of would be to use a ManyToMany with a through model.
class Dm_Company(models.Model):
company_name = models.CharField(max_length=80, blank=True, verbose_name="Razon Social")
company_members = models.ManyToManyField(conf_settings.AUTH_USER_MODEL, through='CompanyMembership')
...
class CompanyMembership(models.Model):
company = models.ForeignKey(Dm_Company)
user = models.ForeignKey(conf_settings.AUTH_USER_MODEL)
is_contact = models.BooleanField(default=False)
The difficulty with this model is that you need to write logic to prevent more than one CompanyMember from being set as is_contact. However, it does structure your data model such that there's no way for the company_contact to reference a user in a different company.
There is no way to filter the company_contact queryset in the way you describe. An alternative is to add the following to your model:
def clean_fields(self, exclude=None):
super().clean_fields(exclude=exclude)
if not self.company_members.exists(id=self.company_contact_id):
raise ValidationError('contact is not member')
That will prevent a contact being selected that is not a member

how to design model for my case with django?

Here are two roles: Trainer and Trainee. Trainer may have multiple trainees. While each trainee may have only one trainer or have no trainer.
Here is my model:
class TrainerShip(models.Model):
trainer = models.ForeignKey('Trainer')
trainee = models.ForeignKey(User)
request_date = models.DateTimeField(auto_now_add=True)
accept_date = models.DateTimeField(auto_now_add=True)
expiration_date = models.DateTimeField(auto_now_add=True)
class Trainer(models.Model):
user = models.ForeignKey(User, unique=True)
trainee = models.ManyToManyField(User, through=TrainerShip)
introduction = models.TextField(max_length=500)
certification = models.TextField(max_length=300)
specialties = models.TextField(max_length=300)
contact = models.TextField(max_length=100)
active = models.BooleanField(default=False)
I was getting following error when trying to create db:
shen#shen-laptop:~/django/sutifang$ ./manage.py syncdb
Error: One or more models did not validate:
registration.trainer: Accessor for field 'user' clashes with related m2m field 'User.trainer_set'. Add a related_name argument to the definition for 'user'.
registration.trainer: Accessor for m2m field 'trainee' clashes with related field 'User.trainer_set'. Add a related_name argument to the definition for 'trainee'.
Anyone has the idea to solve this problem? Is there a better way to model this kind of relationship?
The problem is that a Foreign key established a bidirectional relationship. This means you can do User.trainer_set to get all of the Trainer models under a User, which means you have a circular reference back to the user database (getting the Trainer models gets all of its fields, one of those fields being the original User model.
So, to fix this, add a related name argument to the Foreign key to stop this circular dependency:
user = models.ForeignKey(User, unique=True, related_name='traineruser')
You can replace traineruser with something that does not already have a table in the database.