How can I have manytomany relation among three model classes? - django

from django.contrib.auth.models import User
class UserType1(models.Model):
user = models.OneToOneField(User,parent_link=True,primary_key=True)
class UserType2(models.Model):
user = models.OneToOneField(User,parent_link=True,primary_key=True)
class UserType3(models.Model):
user = models.OneToOneField(User,parent_link=True,primary_key=True)
Each of three types of users has its own unique fields.
I want to let users be able to follow any user he/she wants.
How can I do a manytomany among three different model classes?
Is there a better way of doing this? Maybe an abstract model between Model.auth.User and the three types of users?

Just create a ManyToManyField in each model point to User
class UserType1(models.Model):
user = models.OneToOneField(User, parent_link=True,primary_key=True)
followed_users = models.ManyToManyField(User, related_name='followers')
Do the same with other models. Better yet, create an abstract base class for the common fields in your models. This will save you time and trouble of typing common fields again and again.

Related

Relation between two models in Django

It seems like a trivial question but I am new to Django. I have two models user and competition.
The user can create and join one or many competition.
How to specify this relation as the user can be the owner of competition/s and can be a participant in one or more competition.
I Assume You have two tables User and Competition:
then in competition you can user models.ManyToManyField
Example
class User:
...
class Competition:
...
creator = models.ForeignKey(User)
participents = models.ManyToManyField(User)

Including fields from a OneToOneField in Django Admin

I am attempting to add the fields from a OneToOneField into my admin view. Here is an example of how my models look.
class Customer(BaseUser):
name = CharField()
address = CharField()
secondary_information = OneToOneField("SecondaryCustomerInfo", on_delete=SET_NULL, null=True)
class SecondaryCustomerInfo(models.Model):
email = EmailField()
And I tried adding in the fields as an inline like this.
class SecondaryCustomerInfoInline(admin.StackedInline):
model = SecondaryCustomerInfo
class CustomerAdmin(admin.ModelAdmin):
inlines = [SecondaryCustomerInfoInline]
But I get the error
<class 'user.admin.SecondaryCustomerInfoInline'>: (admin.E202) 'user.SecondaryCustomerInfo' has no ForeignKey to 'user.Customer'.
I'm used to putting the OneToOneField on the secondary model but my coworker asked that I put it on the main Customer model since we will be accessing that information more often. I think switching things around is what is tripping me up. How would I include the fields from SecondaryCustomerInfo on the admin view for Customer?
The answer would be to use Django Reverse Admin
From its documentation:
Module that makes django admin handle OneToOneFields in a better way. A common use case for one-to-one relationships is to "embed" a model inside another one. For example, a Person may have multiple foreign keys pointing to an Address entity, one home address, one business address and so on. Django admin displays those relations using select boxes, letting the user choose which address entity to connect to a person. A more natural way to handle the relationship is using inlines. However, since the foreign key is placed on the owning entity, django admins standard inline classes can't be used.
class CustomerAdmin(ReverseModelAdmin):
inline_type = 'stacked'
inline_reverse = ['secondary_information']

Django models: database design for user and follower

In Django model I am making a table 'followers', which has:
user's id. (this is followed by)
user's id (this is follower)
that's simple a user can follow other users.
How should I define the model in Django?
I tried this, but does not work:
user = models.ForeignKey('self')
follower_id = models.ForeignKey('self')
How should this be done?
thanks
The 'self' argument won't work unless you have a model called self.
Assuming that your assignment model is called Following, and you're using the built in User model then you can do:
class Following(models.Model):
target = models.ForeignKey('User', related_name='followers')
follower = models.ForeignKey('User', related_name='targets')
This will likely need some further uniqueness and validation logic.
Note the related_name attribute, see https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.ForeignKey.related_name. This means that for a given user object you can do user.targets.all() to get users they follow, and user.followers.all() to get users who follow them.
Note also that Django returns target model instances, not IDs, in the ORM. This means that even though the underlying table may be called follower_id, in the python code following.follower will return an actual User object.
Seeing as Following is actually the through table for the many-to-many relationship between Users. I would create a Profile model which extends the Django User model, and then declare the many-to-many relationship (using ManyToManyField).
from django.contrib.auth.models import User
from django.db import models
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
following = models.ManyToManyField(User, related_name='followers')
Use the many to many field.
followers = models.ManyToManyField('self', symmetrical=False)

Django - Multiple User Profiles

Initially, I started my UserProfile like this:
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User)
verified = models.BooleanField()
mobile = models.CharField(max_length=32)
def __unicode__(self):
return self.user.email
Which works nicely along with AUTH_PROFILE_MODULE = 'accounts.UserProfile' set in settings.py.
However, I have two different kinds of users in my website, Individuals and Corporate, each having their own unique attributes. For instance, I would want my Individual users to have a single user only, hence having user = models.OneToOneField(User), and for Corporate I would want them to have multiple users related to the same profile, so I would have user = models.ForeignKey(User) instead.
So I thought about segregating the model into two different models, IndivProfile and CorpProfile, both inheriting from UserProfile while moving the model-specific attributes into the relevant sub-models. Seems like a good idea to me and would probably work, however I would not be able to specify AUTH_PROFILE_MODULE this way since I'm having two user profiles that would be different for different users.
I also thought about doing it the other way around, having UserProfile inherit from multiple classes (models), something like this:
class UserProfile(IndivProfile, CorpProfile):
# some field
def __unicode__(self):
return self.user.email
This way I would set AUTH_PROFILE_MODULE = 'accounts.UserProfile' and solve its problem. But that doesn't look like it's going to work, since inheritance in python works from left to right and all the variables in IndivProfile will be dominant.
Sure I can always have one single model with IndivProfile and CorpProfile variables all mixed in together and then I would use the required ones where necessary. But that is just doesn't look clean to me, I would rather have them segregated and use the appropriate model in the appropriate place.
Any suggestions of a clean way of doing this?
You can do this in following way. Have a profile which will contains common fields which are necessary in both profiles. And you have already done this by creating class UserProfile.
class UserProfile(models.Model):
user = models.ForeignKey(User)
# Some common fields here, which are shared among both corporate and individual profiles
class CorporateUser(models.Model):
profile = models.ForeignKey(UserProfile)
# Corporate fields here
class Meta:
db_table = 'corporate_user'
class IndividualUser(models.Model):
profile = models.ForeignKey(UserProfile)
# Individual user fields here
class Meta:
db_table = 'individual_user'
There is no rocket science involved here. Just have a keyword which will distinguish between corporate profile or individual profile. E.g. Consider that the user is signing up. Then have a field on form which will differentiate whether the user is signing up for corporate or not. And Use that keyword(request parameter) to save the user in respective model.
Then later on when ever you want to check that the profile of user is corporate or individual you can check it by writing a small function.
def is_corporate_profile(profile):
try:
profile.corporate_user
return True
except CorporateUser.DoesNotExist:
return False
# If there is no corporate profile is associated with main profile then it will raise `DoesNotExist` exception and it means its individual profile
# You can use this function as a template function also to use in template
{% if profile|is_corporate_profile %}
Hope this will lead you some where. Thanks!
I have done it this way.
PROFILE_TYPES = (
(u'INDV', 'Individual'),
(u'CORP', 'Corporate'),
)
# used just to define the relation between User and Profile
class UserProfile(models.Model):
user = models.ForeignKey(User)
profile = models.ForeignKey('Profile')
type = models.CharField(choices=PROFILE_TYPES, max_length=16)
# common fields reside here
class Profile(models.Model):
verified = models.BooleanField(default=False)
I ended up using an intermediate table to reflect the relation between two abstract models, User which is already defined in Django, and my Profile model. In case of having attributes that are not common, I will create a new model and relate it to Profile.
Could be worth to try using a through field. The idea behind it is to use the UserProfile model as through model for the CorpProfile or IndivProfile models. That way it is being created as soon as a Corp or Indiv Profile is linked to a user:
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.ForeignKey(User)
profile = models.ForeignKey(Profile, related_name='special_profile')
class Profile(models.Model):
common_property=something
class CorpProfile(Profile):
user=models.ForeignKey(User, through=UserProfile)
corp_property1=someproperty1
corp_property2=someproperty2
class IndivProfile(Profile):
user=models.ForeignKey(User, through=UserProfile, unique=true)
indiv_property1=something
indiv_property2=something
I think that way it should be possible to set AUTH_PROFILE_MODULE = 'accounts.UserProfile', and every time you create either a CorpProfile or a IndivProfile that is linked to a real user a unique UserProfile model is created. You can then access that with db queries or whatever you want.
I haven't tested this, so no guarantees. It may be a little bit hacky, but on the other side i find the idea quite appealing. :)

Show models.ManyToManyField as inline, with the same form as models.ForeignKey inline

I have a model similar to the following (simplified):
models.py
class Sample(models.Model):
name=models.CharField(max_length=200)
class Action(models.Model):
samples=models.ManyToManyField(Sample)
title=models.CharField(max_length=200)
description=models.TextField()
Now, if Action.samples would have been a ForeignKey instead of a ManyToManyField, when I display Action as a TabularInline in Sample in the Django Admin, I would get a number of rows, each containing a nice form to edit or add another Action. However; when I display the above as an inline using the following:
class ActionInline(admin.TabularInline):
model=Action.samples.through
I get a select box listing all available actions, and not a nifty form to create a new Action.
My question is really: How do I display the ManyToMany relation as an inline with a form to input information as described?
In principle it should be possible since, from the Sample's point of view, the situation is identical in both cases; Each Sample has a list of Actions regardless if the relation is a ForeignKey or a ManyToManyRelation. Also; Through the Sample admin page, I never want to choose from existing Actions, only create new or edit old ones.
I see your point but think of a case where you might need to use custom through model (table). In that case the admin inline form would include the fields for that intermediate model since thats the model you asked the admin to create the form for.
e.g.
class Person(models.Model):
name = models.CharField(max_length=128)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
The admin should display the form for the Memebership model cause thats the model the editable instance is related to.
In your case the through model contains only the 2 foreign keys (1 for the Action model and 1 for the Sample) ands thats why only the list of actions appear.
You could do what you are asking for if django admin supported nested inlines (there is an open ticket about that).