Django ORM Count Without Duplicate Column - django

I have a table called Reading History. I want to find the average number of reads in this table. I wrote the following method for this, but I can't get the right result. Records are made in the table once (student-book records with the same values). When the same record comes, the counter value is increased by one.
For example, suppose that two different students read two different books. I expect total reads / 2 but the result I get is total reads / 4 because there are 4 rows in the table. How can I calculate this? For example, if a student reads 4 different books once, the average will be 1, but the average should be 4.
I tried to use distinct and values but I couldn't get the result I wanted. Maybe I didn't manage to use it correctly. Also I tried to use Avg. When avg didn't give the correct result, I tried to break it down and get the values myself. Normally, Avg was written in Sum.
Serializer
class ClassReadingHistoryReportSerializer(ModelSerializer):
instructor = InstructorForClassSerializer(source="instructor.user")
students = StudenListReadingHistoryReportSerializer(many=True,
source="student_list_class")
avg_read_book = serializers.SerializerMethodField()
class Meta:
model = Class
exclude = [
"created_at",
"updated_at",
"school",
]
def get_avg_read_book(self, obj):
sum_read_book = Class.objects.filter(id = obj.id).aggregate(sum_read=Sum('student_list_class__child__child_reading_history__counter'))
count_child_record = Class.objects.filter(id = obj.id).aggregate(count_child=Count('student_list_class__child__child_reading_history'))
return None
Models
class ChildProfile(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
primary_key=True,
verbose_name=AccountStrings.ChildProfileStrings.user_verbose_name,
related_name="user_child")
city = models.ForeignKey(
"country.City",
on_delete=models.SET_NULL,
null=True,
blank=True,
verbose_name=AccountStrings.ChildProfileStrings.city_verbose_name,
related_name="city_child_profiles")
hobbies = models.CharField(
max_length=500,
null=True,
blank=True,
verbose_name=AccountStrings.ChildProfileStrings.hobbies_verbose_name)
class Meta:
verbose_name = AccountStrings.ChildProfileStrings.meta_verbose_name
verbose_name_plural = AccountStrings.ChildProfileStrings.meta_verbose_name_plural
#property
def get_full_name(self):
return f"{self.user.first_name} {self.user.last_name}"
def __str__(self):
return self.get_full_name
def clean(self) -> None:
"""
This method will check if the user type is a child during creation.
"""
if self.user.user_type != 2:
raise ValidationError(AccountStrings.ChildProfileStrings.user_type_error)
class School(AbstractSchoolBaseModel):
city = models.ForeignKey(
"country.City",
on_delete=models.DO_NOTHING,
related_name="city_schools",
verbose_name=SchoolStrings.SchoolStrings.city_verbose_name)
name = models.CharField(
max_length=250,
verbose_name=SchoolStrings.SchoolStrings.name_verbose_name)
address = models.CharField(
max_length=250,
verbose_name=SchoolStrings.SchoolStrings.address_verbose_name)
website = models.CharField(
max_length=250,
verbose_name=SchoolStrings.SchoolStrings.website_verbose_name)
class Meta:
verbose_name = SchoolStrings.SchoolStrings.meta_verbose_name
verbose_name_plural = SchoolStrings.SchoolStrings.meta_verbose_name_plural
def __str__(self):
return self.name
class Class(AbstractSchoolBaseModel):
school = models.ForeignKey(
"school.School",
on_delete=models.CASCADE,
related_name="classes_school",
verbose_name=SchoolStrings.ClassStrings.school_verbose_name)
instructor = models.ForeignKey(
"account.InstructorProfile",
on_delete=models.CASCADE,
related_name="instructors_school",
verbose_name=SchoolStrings.ClassStrings.instructor_verbose_name)
name = models.CharField(
max_length=50,
verbose_name=SchoolStrings.ClassStrings.name_verbose_name)
grade = models.IntegerField(
verbose_name=SchoolStrings.ClassStrings.grade_verbose_name)
class Meta:
verbose_name = SchoolStrings.ClassStrings.meta_verbose_name
verbose_name_plural = SchoolStrings.ClassStrings.meta_verbose_name_plural
ordering = ["name", "grade"]
def __str__(self):
return f"{self.school.name} - {self.name} - Grade: {self.grade}"
def clean(self) -> None:
"""
This method checks whether the teacher trying to be assigned to the class is working in that school.
"""
if self.instructor.school != self.school:
raise ValidationError(SchoolStrings.ClassStrings.instructor_not_working_at_this_school_error)
class StudentList(AbstractSchoolBaseModel):
school_class = models.ForeignKey(
"school.Class",
on_delete=models.CASCADE,
related_name="student_list_class",
verbose_name=SchoolStrings.StudentListStrings.school_class_verbose_name
)
child = models.ForeignKey(
"account.ChildProfile",
on_delete=models.CASCADE,
related_name="student_list_children",
verbose_name=SchoolStrings.StudentListStrings.child_verbose_name)
class Meta:
verbose_name = SchoolStrings.StudentListStrings.meta_verbose_name
verbose_name_plural = SchoolStrings.StudentListStrings.meta_verbose_name_plural
def __str__(self):
return f"{self.school_class.name} : {self.child.user.first_name} {self.child.user.last_name}"
def clean(self) -> None:
result = StudentList.objects.filter(school_class = self.school_class, child = self.child)
if not self.pk and result.exists():
raise ValidationError(SchoolStrings.StudentListStrings.child_already_added_to_this_class_error)
class ReadingHistory(AbstractBookBaseModel):
IS_FINISHED = ((False,
BookStrings.ReadingHistoryStrings.is_finished_false),
(True, BookStrings.ReadingHistoryStrings.is_finished_true))
book = models.ForeignKey(
"book.Book",
on_delete=models.CASCADE,
related_name="book_reading_history",
verbose_name=BookStrings.ReadingHistoryStrings.book_verbose_name)
child = models.ForeignKey(
"account.ChildProfile",
on_delete=models.CASCADE,
related_name="child_reading_history",
verbose_name=BookStrings.ReadingHistoryStrings.child_verbose_name)
is_finished = models.BooleanField(
choices=IS_FINISHED,
verbose_name=BookStrings.ReadingHistoryStrings.is_finished_verbose_name
)
counter = models.PositiveIntegerField(
verbose_name=BookStrings.ReadingHistoryStrings.counter_verbose_name,
default=0)
class Meta:
verbose_name = BookStrings.ReadingHistoryStrings.meta_verbose_name
verbose_name_plural = BookStrings.ReadingHistoryStrings.meta_verbose_name_plural
def __str__(self):
return f"{self.child.user.first_name} {self.child.user.last_name} - \"{self.book.name}\" "
def clean(self) -> None:
result = ReadingHistory.objects.filter(child=self.child,
book=self.book)
if not self.pk and result.exists():
raise ValidationError(
BookStrings.ReadingHistoryStrings.exists_error)
I forgot to add the counter column in the BookReadingHistory table to the diagram. Diagram and Table data are shown in the images below.
Table
Diagram

Related

Django models for creating multiple variations of t-shirts

I'm creating an ecommerce store that sells T-shirts, hoodies, mugs, shot glasses, etc. For the t-shirts and hoodies there are sizes and sometimes color associated with each product. I'm trying to add multiple variations for each product. Here's my model.py code:
class Category(models.Model):
name = models.CharField(max_length = 255, db_index=True, null=True, blank=True)
slug = models.SlugField(max_length=255, unique=True, default='')
class Meta:
verbose_name_plural = 'categories'
def get_absolute_url(self):
return reverse('main:category_list', args=[self.slug])
def __str__(self):
return self.name
class Product(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True, blank=True)
name = models.CharField(max_length = 255, default='')
description = models.TextField(blank=True)
image = models.ImageField(upload_to='products/', null=True, blank=True)
slug = models.SlugField(max_length = 255, default='')
price = models.DecimalField(max_digits=4,decimal_places=2)
update_defaults = models.BooleanField(default=False)
#inventory = models.DecimalField(max_digits=5, decimal_places=None)
class Meta:
ordering=(['name'])
def get_absolute_url(self):
return reverse('main:product_detail', args=[self.slug])
def __str__(self):
return self.name
class VariationManager(models.Manager):
def all(self):
return super(VariationManager, self).filter(active=True)
def sizes(self):
return self.all().filter(category='size')
def colors(self):
return self.all().filter(category='color')
VAR_CATEGORIES = (
('size', 'size'),
('color', 'color'),
)
class Variation(models.Model):
product = models.ForeignKey(Product, related_name="product_attrs", on_delete=models.CASCADE)
category = models.CharField(max_length=120, choices=VAR_CATEGORIES, default='size')
title = models.CharField(max_length=120)
price = models.DecimalField(max_digits=100, decimal_places=2, null=True, blank=True)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
active = models.BooleanField(default=True)
objects = VariationManager()
def __str__(self):
return self.category + " " + self.title
def product_defaults(sender, instance, created, *args, **kwargs):
if instance.update_defaults:
categories = Category.objects.all()
print (categories)
for cat in categories:
print (cat.id)
if cat.id == 1: #for t-shirts
small_size = Variation.objects.get_or_create(product=instance,
category='size',
title='Small')
medium_size = Variation.objects.get_or_create(product=instance,
category='size',
title='Medium')
large_size = Variation.objects.get_or_create(product=instance,
category='size',
title='Large')
XL_size = Variation.objects.get_or_create(product=instance,
category='size',
title='XL')
DoubleXL_size = Variation.objects.get_or_create(product=instance,
category='size',
title='2XL')
TripleXL_size = Variation.objects.get_or_create(product=instance,
category='size',
title='3XL')
instance.update_defaults = False
instance.save()
post_save.connect(product_defaults, sender=Product)
The way it appears right now in my admin interface there is a name, attribute, value, and price (only named relevant fields for clarity). If I add a product like such: "t-shirt_1, size, sm, 17.98", then the next item I need to add is "t-shirt_1, size, med, 17.98" and so forth (2xl and above, price goes up). Is there a way to simplify this where I just enter the product name once, then add all sizes and associated pricing, as well as inventory tracking (haven't created field yet) for each size within the product?
edit:
I've edited my code. I got it figured out on the variations. Now I can't figure out how I could tie inventory quantities into it. If I put it in Product class, it's not specifying what size (i.e. 10 small, 8 medium, 12 Large).
Remove corresponding attribute fields from Product model and create OneToMany relationship between Product and ProductAttribute. Then create separate Size and Color models, and relate them to ProductAttribute with ManyToMany relationship:
class Product(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True, blank=True)
name = models.CharField(max_length = 255, default='')
description = models.TextField(blank=True)
image = models.ImageField(upload_to='products/', null=True, blank=True)
slug = models.SlugField(max_length = 255, default='')
price = models.DecimalField(max_digits=4,decimal_places=2)
has_attributes = models.BooleanField(default=False)
...
class ProductAttribute(models.Model):
product = models.ForeignKey('Product', related_name="product_attrs", on_delete=models.CASCADE)
sizes = models.ManyToManyField('Size', related_name="product_sizes", null=True, blank=True)
colors = models.ManyToManyField('Product', related_name="product_colors", null=True, blank=True)
...
class Size(models.Model):
size_num = models.CharField(max_length=10)
...
class Color(models.Model):
color_name = models.CharField(max_length=15)
...
Now you can create Product object, then go to ProductAttribute to relate corresponding product object with product attribute model and add each attribute of that product (sizes or colors). Whenever you need to get product size or color you can do it as follows:
# let's say you need to get sizes of last product
p = Product.objects.last()
sizes = p.product_attrs.sizes.all()
# ↑ returns queryset containing all sizes of product
Not required
class Attribute(models.Model):
name = models.CharField(max_length = 255, default='')
def __str__(self):
return self.name

price() missing 1 required positional argument: 'self' while trying to calculate the price in Django Rest Framework

I am trying to create a case where when I call the order create api, the price will be calculated itself and saved in the database but I am getting this error in the postman.
Error: price() missing 1 required positional argument: 'self
My models:
class Order(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True)
ordered_date = models.DateTimeField(auto_now_add=True)
ordered = models.BooleanField(default=False)
total_price = models.CharField(max_length=50,blank=True,null=True)
#billing_details = models.OneToOneField('BillingDetails',on_delete=models.CASCADE,null=True,blank=True,related_name="order")
def __str__(self):
return self.user.email
def price(self):
total_item_price = self.quantity * self.item.varaints.price
return total_item_price
class OrderItem(models.Model):
#user = models.ForeignKey(User,on_delete=models.CASCADE, blank=True
#orderItem_ID = models.UUIDField(max_length=12, editable=False,default=str(uuid.uuid4()))
orderItem_ID = models.CharField(max_length=12, editable=False, default=id_generator)
order = models.ForeignKey(Order,on_delete=models.CASCADE, blank=True,null=True,related_name='order_items')
item = models.ForeignKey(Product, on_delete=models.CASCADE,blank=True, null=True)
order_variants = models.ForeignKey(Variants, on_delete=models.CASCADE,blank=True,null=True)
quantity = models.IntegerField(default=1)
total_item_price = models.PositiveIntegerField(blank=True,null=True,default=price())
ORDER_STATUS = (
('To_Ship', 'To Ship',),
('Shipped', 'Shipped',),
('Delivered', 'Delivered',),
('Cancelled', 'Cancelled',),
)
order_item_status = models.CharField(max_length=50,choices=ORDER_STATUS,default='To_Ship')
Here quantity field is present in the OrderItem model itself but the price is present in the Variant model which is related to the Product model like this.
Things I tried:
I tried removing brackets () in price, but got same error.
If I tried putting price function inside class model before total_itel_price field, it says self is required inside bracket of price, and if I put self, = is required and I dont know to put in price(self=??)
Other Models:
Class Variants(models.Model):
...#other fields
price = models.DecimalField(decimal_places=2, max_digits=20,default=500)
Class Product(models.Model):
...#other fields
variants = models.ManyToManyField(Variants,related_name='products')
My serializer:
class OrderSerializer(serializers.ModelSerializer):
billing_details = BillingDetailsSerializer()
order_items = OrderItemSerializer(many=True)
user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
class Meta:
model = Order
fields = ['id','user','ordered_date','order_status', 'ordered', 'order_items', 'total_price','billing_details']
# depth = 1
def create(self, validated_data):
user = self.context['request'].user
if not user.is_seller:
order_items = validated_data.pop('order_items')
billing_details = validated_data.pop('billing_details')
order = Order.objects.create(user=user,**validated_data)
BillingDetails.objects.create(user=user,order=order,**billing_details)
for order_items in order_items:
OrderItem.objects.create(order=order,**order_items)
else:
raise serializers.ValidationError("This is not a customer account.Please login as customer.")
Updated Code:
class OrderItem(models.Model):
#total_item_price = models.PositiveIntegerField(blank=True,null=True,default=0) #commented out this field other fields are same as above
order_item_status = models.CharField(max_length=50,choices=ORDER_STATUS,default='To_Ship')
#property
def price(self):
return self.quantity * self.item.varaints.price
class OrderItemSerializer(serializers.ModelSerializer):
order = serializers.PrimaryKeyRelatedField(read_only=True)
price = serializers.ReadOnlyField()
class Meta:
model = OrderItem
fields = ['id','order','orderItem_ID','item','order_variants', 'quantity','order_item_status','price']
# depth = 1
Order Serializer is just like above. It includes OrderItemSerializer as shown:
class OrderSerializer(serializers.ModelSerializer):
billing_details = BillingDetailsSerializer()
order_items = OrderItemSerializer(many=True)
user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
class Meta:
model = Order
fields = ['id','user','ordered_date','order_status', 'ordered', 'order_items', 'total_price','billing_details']
Update for Order total_price calculation.
This is what I did for total_price calculation but I am not getting total_price field in the api response, there is no error though.
class Order(models.Model):
.....#same fields as above
#property
def total_order_price(self):
return sum([_.price for _ in self.order_items_set.all()])
I have used price function in the OrderItem model and my instance of OrderItem is order_items. What is the issue??
Try this
class Order(models.Model):
"""Stores the details of the order"""
user: User = models.ForeignKey(User, on_delete=models.CASCADE, blank=True)
ordered_date = models.DateTimeField(auto_now_add=True)
ordered = models.BooleanField(default=False)
# billing_details = models.OneToOneField('BillingDetails',on_delete=models.CASCADE,null=True,blank=True,related_name="order")
def __str__(self) -> str:
return self.user.email
#property
def total_price(self) -> int:
"""
Dropped the total price field and created it as property
This is not the best practice, I am leaving that as practice for you :)
"""
return sum([_.total_item_price for _ in self.orderitem_set.all()])
class OrderItem(models.Model):
"""Order Item stores the details of the each order item"""
orderItem_ID: str = models.CharField(
max_length=12, editable=False, default=id_generator
)
order: Order = models.ForeignKey(
Order,
on_delete=models.CASCADE,
blank=True,
null=True,
related_name="order_items",
)
item: Product = models.ForeignKey(
Product, on_delete=models.CASCADE, blank=True, null=True
)
order_variants: Variants = models.ForeignKey(
Variants, on_delete=models.CASCADE, blank=True, null=True
)
quantity: int = models.IntegerField(default=1)
price = models.PositiveIntegerField()
#property
def total_item_price(self):
"""
Calculates total item price for the item
Here you can also add additional logics such as
taxes per item etc
"""
return self.price * self.quantity
ORDER_STATUS = (
("To_Ship", "To Ship"),
("Shipped", "Shipped"),
("Delivered", "Delivered"),
("Cancelled", "Cancelled"),
)
order_item_status = models.CharField(
max_length=50, choices=ORDER_STATUS, default="To_Ship"
)
This code finally worked for OrderItem price calculation:
class OrderItem(models.Model):
.....#fields same as above
#total_item_price = models.PositiveIntegerField(blank=True,null=True,default=0)
ORDER_STATUS = (
('To_Ship', 'To Ship',),
('Shipped', 'Shipped',),
('Delivered', 'Delivered',),
('Cancelled', 'Cancelled',),
)
order_item_status = models.CharField(max_length=50,choices=ORDER_STATUS,default='To_Ship')
#property
def price(self):
total_item_price = self.quantity * self.order_variants.price
return total_item_price
There was a typo in variants. Also, I should be using order_variants instead of item.variants because item has many variants but the user selects only one which has a unique price.

How can I filter manytomany models?

I would like to filter my plots objects on the fruit ex.pear. The Inputs are linked via a manytomany to the plots. This is the structure:
This is the data I get out of it:
What i would like to have:
result:
I tried the following:
plots = Plot.objects.filter(fruittype__fruit="Pear")
inputs = Input.objects.filter(plot__in=plots).distinct()
This gives me already a close solution for my problem but not what I want.
Now I only would like to filter out the other plots that still show up with apple.
models inputs:
class Product (models.Model):
type = models.ForeignKey(Type, on_delete=models.CASCADE)
product = models.CharField(max_length=70)
standaard_dosis = models.FloatField()
def __str__(self):
return self.product
class Input (models.Model):
datum = models.DateField()
plot = models.ManyToManyField(Plot)
def __str__(self):
return str(self.datum)
class ProductInputs (models.Model):
input = models.ForeignKey(Inputs, on_delete=models.CASCADE, default="")
product = models.ForeignKey(Product, on_delete=models.CASCADE, default="")
dosis = models.FloatField()
def __str__(self):
string = str(self.product)
return string
models plots:
class Fruit(models.Model):
fruit = models.CharField(max_length=30, primary_key=True)
def __str__(self):
return self.fruit
class Meta:
verbose_name_plural = "fruits"
class Fruittype(models.Model):
fruit = models.ForeignKey(Fruit, on_delete=models.CASCADE)
fruittype = models.CharField(max_length=30, primary_key=True)
def __str__(self):
return self.fruittype
class Meta:
verbose_name_plural = "fruitypes"
class Plot(models.Model):
name = models.CharField(max_length=30)
fruittype = models.ForeignKey(Fruittype, on_delete=models.CASCADE)
def __str__(self):
return str(self.fruittype.fruit) + " | " + self.name
class Meta:
verbose_name_plural = "plots"
Your Plot queryset is not going as deep as it should. I think you should change to something like this (although this is it's a bit of overkill)
plot_ids = Plot.objects.filter(fruittype__fruit__fruit="Pear").values_list('pk', flat=True)
or
plot_ids = Plot.objects.filter(fruittype__fruittype="Pear").values_list('pk', flat=True) # I don't know what fruittype is but I guess this would help you
Then your "inputs"
inputs = Input.objects.filter(plot__pk__in=plot_ids).distinct()
You might wanna try this as well:
from django.db.models import Prefetch
Input.objects.prefetch_related(
Prefetch('plot', queryset=Plot.objects.filter(fruittype__fruit__fruit="Pear"))
)
It worked with:
all_inputs=Input.objects.filter(plot__pk__in=plot_ids).distinct().prefetch_related(Prefetch('plot', queryset=Plot.objects.filter(fruittype__fruit__fruit="Pear")))

Getting list of many to many objects

class Team(models.Model):
team_name = models.CharField(max_length=50, null=False)
def __str__(self):
return self.team_name
class Tournament(models.Model):
types = (
('Round', 'Round'),
('Knockout', 'Knockout'),
)
teams = models.ManyToManyField(Team, related_name='tournament_teams')
tournament_name = models.CharField(max_length=50, null=False)
tournament_type = models.CharField(choices=types, max_length=40, null=False)
def __str__(self):
return self.tournament_name
class MatchRound(models.Model):
team_a_id = models.ForeignKey(Team, related_name="team_a")
team_b_id = models.ForeignKey(Team)
date = models.DateTimeField(null=True)
team_a_score = models.IntegerField(null=True)
team_b_score = models.IntegerField(null=True)
tournament_id = models.ForeignKey(Tournament, on_delete=models.CASCADE, null=True)
#receiver(post_save, sender=Tournament)
def create_match_round(sender, **kwargs):
type = kwargs.get('instance').tournament_type
if type == 'Round' and kwargs.get('created', False):
teams = kwargs.get('instance').teams.all()
schedule = create_schedule(teams)
for round in schedule:
for match in round:
team_a_id = match[0]
team_b_id = match[1]
tournament_id = kwargs.get('instance')
game = MatchRound.objects.create(team_a_id=team_a_id, team_b_id=team_b_id,
tournament_id=tournament_id)
I am trying to create a schedule for a tournament. So, I set up a trigger on MatchRound model and I am trying to get the teams of the tournament when it's created. However, the following line
teams = kwargs.get('instance').teams.all()
returns to an empty query set. I couldn't figure it out the problem.

Django: How to create object which has ManyToManyField in shell?

I think that showing code is much easier than explanation.
models.py
class Product(TimeStampedModel):
name = models.CharField(max_length=120, unique=True)
slug = models.SlugField(null=True, blank=True)
description = models.TextField(max_length=400, blank=True)
is_active = models.BooleanField(default=True)
place_category = models.ForeignKey(
"PlaceCategory",
related_name="products_by_place", # category.products_by_place.all()
)
subject_category_set = models.ManyToManyField(
"SubjectCategory",
related_name="products_by_subject", # category.products_by_subject.all()
)
objects = ProductManager()
class Meta:
ordering = ('-created',)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse(
"products:product_detail",
kwargs={
"slug": self.slug,
}
)
class Category(TimeStampedModel):
name = models.CharField(max_length=25, unique=True)
is_active = models.BooleanField(default=True)
class Meta:
abstract = True
def __str__(self):
return self.name
class PlaceCategory(Category):
class Meta:
verbose_name = "Place Category"
verbose_name_plural = "Place Categories"
class SubjectCategory(Category):
class Meta:
verbose_name = "Subject Category"
verbose_name_plural = "Subject Categories"
This is what I'm trying to do in shell.
# place category
self.place_category = PlaceCategory.objects.create(name="학교")
# subject category
self.subject_category1 = SubjectCategory.objects.create(name="사람")
self.subject_category2 = SubjectCategory.objects.create(name="꽃병")
# product
self.product = Product.objects.create(
name="product name1",
place_category=self.place_category,
subject_category_set=(
self.subject_category1,
self.subject_category2,
)
)
But it doesn't work. Any idea?
What I could think of is moving ManyToManyField from Product to SubjectCategory.
But I want know as in my code. Thanks.
You need to add subject category to your product.
So do it like this:
# place category
self.place_category = PlaceCategory.objects.create(name="학교")
# subject category
self.subject_category1 = SubjectCategory.objects.create(name="사람")
self.subject_category2 = SubjectCategory.objects.create(name="꽃병")
# product
self.product = Product.objects.create(
name="product name1",
place_category=self.place_category,
)
self.product.subject_category_set.add(self.subject_category1)
self.product.subject_category_set.add(self.subject_category2)
or you can make something like this
list_of_subject_categories = [self.subject_category1, self.subject_category2]
self.product.subject_category_set.add(*list_of_subject_categories)