how to limit_choices_to self field id in django model - django

how use limit_choices_to in django
class Category(models.Model):
"""
商品类别
"""
CATEGORY_TYPE = (
(1, "一级类目"),
(2, "二级类目"),
(3, "三级类目"),
)
def limit_category_type_choice(self):
obj = Category.objects.get(category_type=self.category_type)
field_object = Category._meta.get_field('category_type')
field_value = field_object.value_from_object(obj)
return {'category_type__lte': field_value}
category_id = models.AutoField(primary_key=True, verbose_name='category_id')
category_title = models.CharField(default='', max_length=50, verbose_name='目录标题', help_text='目录标题')
category_name = models.ForeignKey(LinkDesignate,blank=True, null=True, to_field='link_des_text_id', related_name='category', on_delete=models.CASCADE)
category_type = models.IntegerField(choices=CATEGORY_TYPE, verbose_name="类目级别", help_text="类目级别")
parent_category = models.ForeignKey("self", limit_choices_to=self.limit_category_type_choice, null=True, blank=True, verbose_name="父类目级别", help_text="父目录",
related_name="sub_cat", on_delete=models.CASCADE)
class Meta:
verbose_name = "产品目录"
verbose_name_plural = verbose_name
i know this
limit_choices_to=self.limit_category_type_choice,
this is wrong , because name 'self' is not defined
how can to use the function limit_category_type_choice
the document is:
def limit_pub_date_choices():
return {'pub_date__lte': datetime.date.utcnow()}
limit_choices_to = limit_pub_date_choices
how can i change my Function limit_category_type_choice without self
but can use the self instance

Put the def out of the class ;-)
This was my solution, it is similar.
It limits the films which can be choosen for an event.
Films with status '2' in class Film over ForeignKey OR
over reverse ForeignKey: Films with an Event with date in the future
the model:
def limit_film_choices():
date = str(datetime.datetime.now())
result = Q( status = '2') | Q( event_film__date__gte = date)
return result
class Film(models.Model):
STATUS_COICES = [
('1' , 'a'),
('2' , 'b'),
]
status = models.CharField( max_length=16, choices=STATUS_COICES, default='1')
class Event(models.Model):
film = models.ForeignKey('filme.Film', on_delete=models.CASCADE, related_query_name='event_film',
related_name='event_film', blank = True, null=True, limit_choices_to = limit_film_choices)
date = models.DateTimeField(auto_now=False, null=True)

Related

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 to get a non-pk value to be the value by which a post request is made in django rest framework

As the question states, I'd like to be able to instead of having to pass a PK inside my JSON request in the post request I could pass a different value, like a username "bob" instead of 1.
so the idea is that instead of my request containing:
{
"client": 1,
"urgent": true,
"article": 1,
"quantity": 1,
"order_to": 1
}
it should contain:
{
"client": "bob",
"urgent": true,
"article": 234,
"quantity": 1,
"order_to": 1
}
here are my relevant models:
class UserModel(models.Model):
MEMBERSHIP_TYPE = [
('NM', 'NORMAL'),
('PT', 'PLATA'),
('OR', 'ORO'),
('PL', 'PLATINO'),
]
id = models.AutoField(primary_key=True)
username = models.CharField(max_length=100, unique=True)
photo = models.ImageField(upload_to='images/', blank=True)
address = models.TextField()
client_type = models.CharField(max_length=2,
choices=MEMBERSHIP_TYPE,
default= 'NM')
def __unicode__(self):
return self.name
class ArticleModel(models.Model):
id = models.AutoField(primary_key=True)
code = models.IntegerField(unique=True)
description = models.TextField()
def __str__(self):
return str(self.code)
class SupplierModel(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
address = models.TextField()
articles = models.ManyToManyField(ArticleModel)
def __str__(self):
return self.name
class OrderModel(models.Model):
id = models.AutoField(primary_key=True)
client = models.ForeignKey('UserModel', on_delete=models.CASCADE)
gen_time = models.DateTimeField(auto_now_add=True)
gen_supplied = models.DateTimeField(null=True, blank=True)
urgent = models.BooleanField()
order_to = models.ForeignKey('OrderToModel', on_delete=models.CASCADE)
article = models.ForeignKey('ArticleModel', on_delete=models.CASCADE)
quantity= models.IntegerField()
and Serializer:
class OrderCreateSerializer(serializers.ModelSerializer):
# order_to = OrderToPolymorphicSerializer()
class Meta:
model = OrderModel
fields = ('client', 'urgent', 'article', 'quantity', 'order_to')
Help is much appreciated.
Thank you in advance.
you can do this
class OrderCreateSerializer(serializers.ModelSerializer):
client = serializers.SlugRelatedField(
slug_field='username',
queryset=UserModel.objects.all()
)
article = serializers.SlugRelatedField(
slug_field='code',
queryset=ArticleModel.objects.all()
)
class Meta:
model = OrderModel
fields = ('client', 'urgent', 'article', 'quantity', 'order_to')

'NoneType' object has no attribute 'name' django

I have three model CustomUser, Profile and artist. what i want to achieve that when i create customuser it should create userprofile and artist also when i update userprofile field it should also update artist field.
CustomUser Model:
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=100, unique=True)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_superuser = models.BooleanField(default=False)
last_login=models.DateTimeField(null=True, blank=True)
date_joined = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'email'
EMAIL_FIELD='email'
REQUIRED_FIELDS=[]
objects=UserManager()
def get_absolute_url(self):
return "/users/%i/" % (self.pk)
Artist model:
class Artist(models.Model):
CHOICES = (
(0, 'celebrities'),
(1, 'singer'),
(2, 'comedian'),
(3, 'dancer'),
(4, 'model'),
(5, 'Photographer')
)
#userprofile = models.ForeignKey(UserProfile,null=True,on_delete = models.CASCADE)
user = models.OneToOneField(CustomUser,null = True, on_delete = models.CASCADE)
name = models.CharField(max_length=100,null= True)
artist_category = models.IntegerField(choices = CHOICES, null=True)
artist_image = models.ImageField(upload_to= 'media',null=True)
bio = models.TextField(max_length = 500)
def __str__(self):
return str(self.name)
UserProfile Model:
class UserProfile(models.Model):
CHOICES = (
(0, 'celebrities'),
(1, 'singer'),
(2, 'comedian'),
(3, 'dancer'),
(4, 'model'),
(5, 'Photographer')
)
user = models.OneToOneField(CustomUser, related_name='userprofile', on_delete= models.CASCADE)
artist = models.ForeignKey(Artist,related_name='userprofile', on_delete=models.CASCADE, null=True)
name = models.CharField(max_length=100, null=True)
artist_category = models.IntegerField(choices= CHOICES, null=True)
mobile_number = PhoneNumberField(null=True)
country = CountryField(default = 'IN')
city = models.CharField(blank=True, max_length=100)
bio = models.TextField(blank=True)
def __str__(self):
return str(self.user)
creating User Profile:
def create_profile(sender,instance, created,**kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_profile,sender=CustomUser)
#receiver(post_save, sender= CustomUser)
def save_profile(sender,instance,**kwargs):
instance.userprofile.save()
creating artist:
def create_artist(sender,instance,created,**kwargs):
if created:
Artist.objects.create(user = instance)
post_save.connect(create_artist,sender=CustomUser)
everything working till here.
updating artist from userprofile:
def update_user_artist(sender, **kwargs):
instance = kwargs['instance']
instance.artist.name = instance.name
instance.artist.save()
post_save.connect(update_user_artist,sender = UserProfile)
this part getting none type object error:
File "/home/tboss/Desktop/environment/celeb/celeb/main_site /models.py", line 175, in update_user_artist
instance.artist.name = instance.name
AttributeError: 'NoneType' object has no attribute 'name'

I need to display summary report in Django admin site. How do I count number of users where gender status is 0 or 1

I am overriding change_list.html and here is what I have in my admin.py file.
class MyHelperGenderAdmin(admin.ModelAdmin):
change_list_template = 'admin/helper_chart_change_list.html'
date_hierarchy = 'created_at'
list_filter = ('gender', 'created_at')
def changelist_view(self, request, extra_context=None):
response = super().changelist_view(request, extra_context=extra_context, )
try:
qs = response.context_data['cl'].queryset
except (AttributeError, KeyError):
return response
metrics = {
'male': Count('gender', gender=1),
'female': Count('gender', gender=0),
'total_helpers': Count('id')
}
response.context_data['helper'] = list(
qs.values('gender').annotate(**metrics).order_by('-male')
)
return response
def has_add_permission(self, request):
return False
admin.site.register(MyHelperChart, MyHelperGenderAdmin)
In my metrics dictionary, i need a way to count where gender is either 0 or 1. Currently, The count method count everything regards of the gender status.
Here is my model:
class Helper(auth.models.User):
MALE = 1
FEMALE = 0
GENDER_CHOICES = (
(MALE, 'Male'),
(FEMALE, 'Female')
)
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$',
message=
"Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed."
)
phone_number = models.CharField(validators=[phone_regex], max_length=15, blank=True, unique=True, null=True)
gender = models.IntegerField(choices=GENDER_CHOICES, default=MALE, null=True)
birthdate = models.DateField(blank=True, null=True)
facebook_id = models.CharField(max_length=200, blank=True, null=True)
google_id = models.CharField(max_length=200, blank=True, null=True)
lng = models.FloatField(blank=True, null=True)
lat = models.FloatField(blank=True, null=True)
country = models.ForeignKey(Country, blank=True, null=True)
image = models.ImageField(
upload_to=upload_location,
null=True, blank=True,
width_field="width_field",
height_field="height_field")
height_field = models.IntegerField(default=0)
width_field = models.IntegerField(default=0)
updated_at = models.DateTimeField(auto_now=True)
created_at = models.DateTimeField(auto_now=False, auto_now_add=True)
class Meta:
verbose_name_plural = "Helpers"
def __str__(self):
return self.first_name
class MyHelperChart(Helper):
class Meta:
proxy = True
verbose_name = 'Helper Gender Summmary'
verbose_name_plural = 'Helpers Gender Summaries'
I have created a proxy model which I will be using in my Django admin in order to display summary of data and a chart.
All you need to do is add a case to both the male and female metrics. Here's an example, and then you can do the same for female = 0:
from django.db.models import Case, When, IntegerField
class MyHelperGenderAdmin(admin.ModelAdmin):
...
metrics = {
...
'male': Count(Case(
When(gender = 1, then = 1),
output_field = IntegerField())),
...
}

DRF PUT request on unique model field

I have the following model:
class Movie(models.Model):
name = models.CharField(max_length=800, unique=True)
imdb_rating = models.IntegerField(null=True)
movie_choice = (
('Act', 'Action'),
...........
)
movie_type = models.CharField(max_length=3, choices=movie_choice)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Hiren(models.Model):
movie = models.ForeignKey(Movie)
watched_full = models.BooleanField(default=True)
rating = models.IntegerField()
source = models.CharField(max_length=500, null=True)
watched_at = models.DateField()
quality_choice = (
..................
)
video_quality = models.CharField(max_length=3, choices=quality_choice)
created_at = models.DateField(auto_now_add=True)
updated_at = models.DateField(auto_now=True)
and serializer:
class MovieSerializer(serializers.ModelSerializer):
class Meta:
model = Movie
fields = '__all__'
class HirenSerializer(serializers.ModelSerializer):
movie = MovieSerializer()
class Meta:
model = Hiren
fields = ('movie', 'id', 'watched_full', 'rating', 'source', 'video_quality', 'watched_at')
def update(self, instance, validated_data):
instance.movie.name = validated_data.get('movie', {}).get('name')
instance.movie.imdb_rating = validated_data.get('movie', {}).get('imdb_rating')
instance.movie.movie_type = validated_data.get('movie', {}).get('movie_type')
instance.watched_full = validated_data.get('watched_full', instance.watched_full)
instance.rating = validated_data.get('rating', instance.rating)
instance.source = validated_data.get('source', instance.source)
instance.video_quality = validated_data.get('video_quality', instance.video_quality)
instance.watched_at = validated_data.get('watched_at', instance.watched_at)
instance.movie.save()
instance.save()
return instance
When I try to send a put request without changing name field from Movie model it throws an error
{
"movie": {
"name": [
"movie with this name already exists."
]
}
}
However, I can perfectly update any other field if I change the name field's value each time.
The problem is in Movie model defined by you.
When you set the name field of Movie model as unique = True,then any new entry with same movie name will always throw an error.
In your model,
class Movie(models.Model):
name = models.CharField(max_length=800, unique=True)
imdb_rating = models.IntegerField(null=True)
movie_choice = (
('Act', 'Action'),
...........
)
movie_type = models.CharField(max_length=3, choices=movie_choice)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
If you want to add two entries with the same name,remove the line unique = True or make sure to save every entry with a different name.
Or,if you want to update the record/entry then you don't need to assign a value for name field,just remove that line from your code,alternatively check if the name of the movie is already same with an improvement in the code like this :
class HirenSerializer(serializers.ModelSerializer):
movie = MovieSerializer()
class Meta:
model = Hiren
fields = ('movie', 'id', 'watched_full', 'rating', 'source', 'video_quality', 'watched_at')
def update(self, instance, validated_data):
movie_name = validated_data.get('movie', {}).get('name')
if movie_name != instance.movie.name :
instance.movie.name = movie_name
instance.movie.imdb_rating = validated_data.get('movie', {}).get('imdb_rating')
instance.movie.movie_type = validated_data.get('movie', {}).get('movie_type')
instance.watched_full = validated_data.get('watched_full', instance.watched_full)
instance.rating = validated_data.get('rating', instance.rating)
instance.source = validated_data.get('source', instance.source)
instance.video_quality = validated_data.get('video_quality', instance.video_quality)
instance.watched_at = validated_data.get('watched_at', instance.watched_at)
instance.save()
return instance
Hope this helps,Thanks.