How do I create a 'couple' relationship in Django? - python

I've got a model called Couple. Couple describes the relationship between two Auth.User records.
class Couple(models.Model):
created = models.DateTimeField(auto_now_add=True)
partner_one = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING, related_name='partner_one')
partner_two = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING, related_name='partner_two')
I'm having trouble referencing the partner in a reliable from their user model because now I need to know which is partner one, partner two. This is obviously not an elegant solution. Is there a better way to achieve what I'm trying to do here?

Related

Creating A Relationship Between Two User Models

Im building a website which will have teachers and students. Im using the default Django User models and originally ignored Teachers and treated all my Users as Students. Today I began trying to separate my users into Teachers and Students and am having a lot of difficulty. Im obviously missing some fundamental knowledge and have read a lot online but am going around in circles.
A teacher can have many students and a student can have one teacher.
First I thought Id need separate Student and Teacher models. So everyone now is a User, and I will attach either a Student or Teacher model to each (is this sloppy?).
Now for the relationship. I tried creating a relationship between teachers and students within their own models, but it didnt work so I figured id need a separate TeacherStudentRelationship class to hold the relationships.
This is my implementation so far:
class Student(models.Model):
student = models.OneToOneField(User, on_delete=models.CASCADE)
def __str__(self):
return f'{self.student.username}'
class Teacher(models.Model):
teacher= models.OneToOneField(User, on_delete=models.CASCADE)
def __str__(self):
return f'{self.teacher.username}'
class TeacherStudentRelationship(models.Model):
student = models.ForeignKey(User, on_delete=models.CASCADE)
teacher = models.OneToOneField(User, on_delete=models.CASCADE)
This doesnt throw any errors, though in Django Admin I can create multiple instances of TeacherStudentRelationship and assign a student to a teacher in multiple instances (should only be able to assign a student to a teacher once). I can also only assign a single student to a single teacher within one instance even though the relationship is one-to-many.
I also have a problem with my implementation. I have a signal fire every time a user is created which generates a Student model and links it with the User. This is fine when I have students enrolling on my site, though it means my Admin also needs a Student model (or else it throws errors). I also planned on creating Teachers in the Django Admin panel, but this will cause issues there too as they will need Student models. How would you implement this?
Thank you.
Instead of having a separate relationship like this, you can have a ManyToMany Relation in your teacher model. Like this:
class Student(models.Model):
student = models.OneToOneField(User, on_delete=models.CASCADE)
def __str__(self):
return f'{self.student.username}'
class Teacher(models.Model):
teacher= models.OneToOneField(User, on_delete=models.CASCADE)
students = models.ManyToManyField(Student)
In that way, it won't need to create new student when you are create a Teacher instance or vice versa.
Update
From comments, unfortunately you can't remove signals unless you use custom User model in django. You can try like this:
CHOICES = (('student', "Student"), ('teacher', "Teacher"))
class User(AbstractUser):
user_type = models.CharField(choices=CHOICES, max_length=255)
has_teacher = models.ForeignKey('self', null=True, default=None)
REQUIRED_FIELDS = ['user_type', 'email']
Then, you can define what kind of user it is during the User creating process, hence you do not need the signals. Also, you can store auth data of student and teacher in same model.

Defining a limit_choices_to for a OneToOneField which references the current model

I have two models, Company and Package, with a many-to-one relationship between them: each Company can have several Packages, but each Package has only one Company.
In addition, however, I'd like to define a default_package field for the Company model, which is a Package, and I'd like to limit the choices to the packages whose Company is the company under consideration.
class Company(models.Model):
default_package = models.OneToOneField(
'dashboard.Package',
on_delete=models.SET_NULL,
blank=True,
null=True,
related_name='default_for_%(class)s')
class Package(models.Model):
company = models.ForeignKey(Company, on_delete=models.CASCADE)
However, I'd like to add a limit_choices_to argument to the default_package field which would be something like
default_package = models.OneToOneField(..., limit_choices_to={'company': <this_company>})
where <this_company> is a reference to the current company. I'm not what the syntax for this would be, though; can anyone offer some help?
It would appear that this question was answered in How to get instance of entity in limit_choices_to (Django)?. In short, this is not possible.

Creating a model related to an arbitrary number of other models

I deleted my previous question, since it was terribly worded and my non-working examples were just confusing.
I have a series of models, such as Vehicle, Computer, Chair, and whatnot. My goal is to be able to attach an arbitrary number of images to each of them. That's my question: what's the best design pattern to achieve this?
What I've tried so far is to create an abstract model, AttachedImage. I then inherit from this model, and create more specific models, like VehicleImage, ComputerImage, or ChairImage. But this doesn't feel like the right way to pursue this.
As I mentioned in the comment on the deleted question, the correct way to do this would be to use generic relations.
Make your AttachedImage model concrete, but add content_type and object_id fields and the content_object GenericForeignKey. That content_object field can now point to an instance of any model.
To make the reverse relationships easier, you can add GenericRelation accessors to your Vehicle, Computer and Chair models.
Thanks to Daniel Roseman's, I found out about generic relationships. You can find a great overview and tutorial about them here. There are basically two ways to achieve this.
The first one is using generic relationships as explained in the tutorial I linked. This system is very versatile and powerful, but in my case, this would add an additional layer of complexity that quite frankly I don't need.
If you're a newbie with Django like I am, you can consider a much more straightforward, but less 'systematic' pattern, also detailed in that tutorial I linked: simply add a ForeignKey field for each one of the objects you want your main model to be related to. Following the example I used in my question:
class AttachedImage(models.Model):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
author = models.ForeignKey(User, on_delete=models.CASCADE)
image = models.ImageField(upload_to=image_path) # TO FIX: aƱadir la ruta
is_default = models.BooleanField(default=False)
To this, you just add fields for the relationships you will need, such as:
parent_vehicle = models.ForeignKey(Vehicle, on_delete=models.CASCADE, null=True)
parent_computer = models.ForeignKey(Computer, on_delete=models.CASCADE, null=True)
parent_chair = models.ForeignKey(Chair, on_delete=models.CASCADE, null=True)
And just use them as you regularly would. The downside is ending up with all those extra fields you don't really need, but being NULLable, they won't take up much space. The other downside is that doing this with a big number of models is probably overkill, in which case you should really use the first solution.
If you don't want to use the GenericForeignKey, you can implement your own polymorphic models, with multiple table inheritance. Following are the pseudo-models for the same:
class Attachable(models.Model):
pass
class Image(models.Model):
attachable = models.ForeignKey(Attachable, related_name='images')
class Video(models.Model):
attachable = models.ForeignKey(Attachable, related_name='videos')
class Vehicle(Attachable):
vehicle attrs...
class Computer(Attachable):
computer attrs...
class Chair(Attachable):
chair attrs...
For later optimizations, Attachable can also have an attribute which describes its subtype.

Python Django models basics

I am learning django and trying to create a simple Question-Answer app where users can also like the questions and/or answers. Now what I want to do is track who liked a particular question/answer so I created a separate model for tracking this as follows:
class Votes(models.Model):
answer_id = models.ForeignKey(Answers, related_name='likes_answers', null=True, default=None, db_column='answer_id')
question_id = models.ForeignKey(Questions, related_name='likes_questions', null=True, default=None, db_column='question_id')
likes_count = models.IntegerField(blank=True, default=0)
liked_by = models.ForeignKey(User, related_name='like_user', null=True, default=None, on_delete=models.CASCADE)
Now the problem with this approach is that everytime a question or an answer is liked there will be an entry to this model which will create multiple entries of same question_id or answer_id with different users. Although the users are different, but a lot of answer_ids and question_ids are getting repeated...Is that right approach or can I make it better?
I guess a better approach would be using many-to-many fields: users are able to like multiple questions/answers, and questions/answers may be liked by multiple users.
Assuming your Answer has a foreign key to a particular question, the models may look like this:
class Question(models.Model):
# .. other attributes
liked_by = models.ManyToManyField(User, related_name = 'liked_question')
class Answer(models.Model):
question = models.ForeignKey(Question)
# .. other attributes
liked_by = models.ManyToManyField(User, related_name = 'liked_answer')
To get all users that liked the answer, you use
answer_object.liked_by.all()
For the (often-used) likes count, you would simply use
answer_object.liked_by.count()
Many-to-many fields work the other way around too, so to determine likes by a user, you may use (thanks to the related name)
user_object.liked_question_set.all()
user_object.liked_answer_set.all()
It's not a python, django question, it's fit an RDBMS design question.
But to answer your question,
If you want to check the duplicate from voting by user then it's right approach.
Other approach is update the voting post id to the user table. It's of course bad approach.

Querying for followers in news-feed-based data model in Django

I have this following (simplified) model in Django which is very similar to the Pinterest data model:
class UserProfile(models.Model):
user = models.OneToOneField(User)
class Collection(models.Model):
owner = models.ForeignKey(User,related_name='collection_owner')
followers = models.ManyToManyField(User, related_name='collection_followers', null=True, blank=True, default = None)
class Item(models.Model):
collections = models.ManyToManyField(Collection,blank=True,null=True)
I have a User model, a UserProfile model that maps 1-1 with a user, a Collection model which has an owner and followers and items that can be part of multiple collections. I'm struggling with determining how to execute the following queries in Django:
Get all the followers of a given user. The definition of a follower is one that follows at least one collection that is owned by that particular user.
Get all the distinct items of the collection a user follows.
I'm not sure if I can do these in a single query or do I have to break it up in several queries? What would be the best approach and are there any trade offs?
Thanks for any help.
The first would be something like this I think:
User.objects.filter(collection_owner__owner='the user')
The latter should be something like this:
Item.objects.filter(collections__followers='the user').distinct()
You should be aware however that these type of queries do not scale to large amounts of data. Doing that will require quite a bit of hacking...

Categories