Django Many-to-Many Relation or One-to-Many relation - python

i have this tables in my django :
class User(models.Model):
username = models.CharField(max_length=40)
class Photo(models.Model):
publish_by = models.ForeignKey(User)
name = models.CharField(max_length=40)
desc = models.CharField(max_length=40)
User could publish Phtotos , and they can like Photos .but i don't know how to write the like in the phtoto , should i use one to many or many to many ?
and how could i get the Users that like a Photo .
thanks for help .
UPDATE
In the end I decided to use a many to many with a through model because I also wanted to record the time. The models I have settled on are these
class User(models.Model):
username = models.CharField(max_length=40)
class Photo(Model):
author = models.ForeignKey(User, related_name='%(class)ss')
publish_time = models.DateTimeField(default=datetime.datetime.now)
liked_by = models.ManyToManyField(User, related_name="likes",through='PhotoLike',)
def like(self, user):
liked, created = PhotoLike.objects.get_or_create(photo=self,user=user)
return liked
def save(self, *args, **kwargs):
super(Photo, self).save(*args, **kwargs)
class Meta:
app_label = 'meinv'
class PhotoLike(models.Model):
user = models.ForeignKey(User)
photo = models.ForeignKey(Photo)
like_time = models.DateTimeField(default=datetime.datetime.now)
class Meta:
app_label = 'meinv'

You just need to think about how photos are liked.
Can a user like many photos?
Can many photos be liked by the one user?
Then it is a many to many.
You would implement it like this
class Photo(models.Model):
publish_by = models.ForeignKey(User)
name = models.CharField(max_length=40)
desc = models.CharField(max_length=40)
liked_by = models.ManyToManyField(User, related_name="likes")
Then, it works like this, you can add likes to a photo by
photoInstance.liked_by.add(user)
Access the likes of a photo this way
photoInstance.liked_by.all()
To get all the photos a user liked
user.likes.all()

class Like(models.Model):
user = models.ForeignKey(User)
photo = models.ForeignKey(Photo)

Related

How to get specific objects based on ManyToMany field match

I'm doing a cookbook app, which help users find meal thay can do with their ingridients. I'm using Django RestFramework, and i need to return list of avaliable meals that user can do, but don't know how to do search by ingridients
My models.py:
#models.py
class Meal(models.Model):
name = models.CharField(max_length=250)
description = models.TextField(blank=True, null=True)
recipe = models.TextField()
is_published = models.BooleanField(default=False)
category = ForeignKey('Category', on_delete=models.CASCADE, null=True)
user = ForeignKey(User, verbose_name='User', on_delete= models.CASCADE)
difficulty = ForeignKey('Difficulty', on_delete=models.PROTECT, null=True)
ingridients = models.ManyToManyField('Ingridient')
class Ingridient(models.Model):
name = models.CharField(max_length=100, db_index=True)
ico = models.ImageField(upload_to="photos/%Y/%m/%d/", blank=True, null=True)
category = ForeignKey('CategoryIngridients', on_delete=models.CASCADE, null=True)
def __str__(self):
return self.name
class CookBookUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
ingridients = models.ManyToManyField('Ingridient')
serializer.py
class MealSerializer(serializers.ModelSerializer):
class Meta:
model = Meal
fields = "__all__"
views.py
class CraftWithUsersIngridientsListAPIView(generics.ListAPIView):
serializer_class = MealSerializer
def get_queryset(self):
return Meal.objects.filter(ingridients=CookBookUser.objects.filter(user_id = self.request.user.id).ingridients)
CraftWithUsersIngridientsListAPIView isn't working and I get AttributeError 'QuerySet' object has no attribute 'ingridients', can someone help fix this?
I tried building different serializer but it doesn't help
class CraftWithUsersIngridientsListAPIView(generics.ListAPIView):
serializer_class = MealSerializer
def get_queryset(self):
user_ingredients = CookBookUser.objects.get(user=self.request.user).ingredients.all()
return Meal.objects.filter(ingredients__in=user_ingredients)
This way, you first get the CookBookUser instance for the current user, then get all of their ingredients, and finally, filter the Meal objects that contain those ingredients. The __in query lookup is used to check if the meal ingredients are in the user's ingredients.

Getting AttribueError by SlugRelatedField despite the object being saved

I am creating an API to save class teachers. Now all the fields in the ClassTeacher model are foreign fields so I am using a SlugRelatedField in the serializer. It looks like SlugRelatedField does not support attribute lookup like this "user__username" and raises attribute error HOWEVER the object is still being saved.
models.py
class ClassTeacher(models.Model):
teacher = models.ForeignKey(Teacher, on_delete=models.CASCADE)
class_name = models.ForeignKey(Classes, on_delete=models.CASCADE)
school_id = models.ForeignKey(School, on_delete=models.CASCADE)
serializers.py
class ClassTeacherSerializer(ModelSerializer):
teacher = SlugRelatedField(slug_field='user__username', queryset=Teacher.objects.all()) <---- this is causing the error
class_name = SlugRelatedField(slug_field='class_name', queryset=Classes.objects.all())
school_id = SlugRelatedField(slug_field='school_id__username', queryset=School.objects.all()) <---- and I am assuming that this will too
class Meta:
model = ClassTeacher
fields = '__all__'
I tried adding a #property in the Teacher model to retrieve the username and use the property in the slug_field but that did not work too.
How can I save the object without getting the error?
EDIT 1:
teachers/models.py
class Teacher(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=30)
photo = models.URLField()
teacher/serializers.py
class TeacherSerializer(ModelSerializer):
class Meta:
model = Teacher
fields = '__all__'
school/models.py
class School(models.Model):
school_id = models.OneToOneField(User, on_delete=models.CASCADE)
principal = models.CharField(max_length=50)
name = models.CharField(max_length=50)
photo = models.URLField()
school/serializers.py
class SchoolSerializer(ModelSerializer):
class Meta:
model = School
fields = '__all__'
EDIT 2:
Here's how I used the #property by referring from here:
teacher/models.py
class Teacher(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=30)
photo = models.URLField()
#Here's the extra property part
#property
def username(self):
return self.user.username
classteacher/serializers.py
class ClassTeacherSerializer(ModelSerializer):
#Here I changed user__username to just username as mentioned in the above link
teacher = SlugRelatedField(slug_field='username', queryset=Teacher.objects.all())
class_name = SlugRelatedField(slug_field='class_name', queryset=Classes.objects.all())
school_id = SlugRelatedField(slug_field='school_id__username', queryset=School.objects.all())
class Meta:
model = ClassTeacher
fields = '__all__'
try renaming serializer field from teacher to user and using slug_field='username'
You can use #property for example
class User(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=30)
photo = models.URLField()
#property
def get_username(self):
return Teacher.objects.filter(user_id=self.id)
and than inside your ClassTeacherSerializer use slug_field='username'
let me know if it works.

check if object in ManyToMany field django rest framework

here is my models.py:
class Post(models.Model):
id = models.AutoField(primary_key=True)
body = models.TextField(max_length=10000)
date = models.DateTimeField(auto_now_add=True, blank=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
liked_by = models.ManyToManyField(User, blank=True, related_name='liked_by')
class Meta:
ordering = ['-date']
serializers.py:
class PostSerializer(serializers.ModelSerializer):
user = UserSerializers()
total_likes = serializers.SerializerMethodField()
total_comments = serializers.SerializerMethodField()
class Meta:
model = Post
fields = ('id','body','date','user','total_likes','total_comments')
def get_total_likes(self, instance):
return instance.liked_by.count()
def get_total_comments(self, instance):
return instance.comment_set.count()
Q1: how do i check if a user exists in ManyToManyField of a single post?
Q2: shouldn't i use ManyToManyField in drf? then which would be better?
I don't have enough reps to comment, but if you have a post instance and a user instance, you could do something like:
post.liked_by.filter(id=user.id).exists()
Does that help you or are you asking where you should be implementing this? e.g. in your view or serializer etc...

Roles in django model

I have a model in django, in which there will be several roles - regular user, admin and manager. Each of them will be able to do something else. Is the following model OK to work correctly?
class Team(models.Model):
name = models.CharField('Name', max_length=128)
users = models.ManyToManyField(User)
admins = models.ManyToManyField(User)
managers = models.ManyToManyField(User)
def __str__(self):
return self.name
This might work. If the number of roles is large, or dynamic (as in roles can be added, removed, renamed, updated). You could introduce a ternary relation, like:
from django.conf import settings
class Role(models.Model):
name = models.CharField(max_length=128)
class Team(models.Model):
name = models.CharField('Name', max_length=128)
members = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Membership')
def __str__(self):
return self.name
class Membership(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
team = models.ForeignKey(Team, on_delete=models.CASCADE)
role = models.ForeignKey(Role, on_delete=models.PROTECT)

How to make two Django models with cross relationship?

I need two Django models: first with Users, second with Projects.
Between them I need many-to-many relationship with an additional field(s).
How to make the below code working?
from django.db import models
class User(models.Model):
name = models.CharField('Name', max_length=50)
projects = models.ManyToManyField(Project, through='UserProjects')
def __str__(self):
return self.name
class Project(models.Model):
name = models.CharField('Name', max_length=50)
users = models.ManyToManyField(User, through='UserProjects')
def __str__(self):
return self.name
class UserProjects(models.Model):
user = models.ForeignKey(User)
project = models.ForeignKey(Project)
is_active = models.BooleanField('Active')
At the end User.projects should return Projects for specified User
and in the same way Project.users should return Users for specified Project.
There's no need to put the m2m field on both sides
Jussi pick one, and Django will automatically create a reverse relationship for the other direction.
from django.db import models
class User(models.Model):
name = models.CharField('Name', max_length=50)
def __str__(self):
return self.name
class Project(models.Model):
name = models.CharField('Name', max_length=50)
users = models.ManyToManyField(User, through='UserProjects', related_name='projects')
def __str__(self):
return self.name
class UserProjects(models.Model):
user = models.ForeignKey(User)
project = models.ForeignKey(Project)
is_active = models.BooleanField('Active')
Here is the simplest solution in my opinion:
from django.db import models
class User(models.Model):
name = models.CharField('Name', max_length=50)
class Project(models.Model):
name = models.CharField('Name', max_length=50)
class UserProjects(models.Model):
user = models.ForeignKey(User, related_name='projects')
project = models.ForeignKey(Project, related_name='users')
is_active = models.BooleanField('Active')
In above User.projects returns Projects for specified User and also Project.users returns Users for specified Project.

Categories