Django admin filter foreign key - object

I have 4 class:
class Material(models.Model):
name = models.CharField(_('name'), max_length=50)
class Property(models.Model):
name = models.CharField(_('name'), max_length=50)
class Physic(models.Model):
name = models.ForeignKey(Property, verbose_name=_('name'), null=True, blank=True,)
lapropriete = models.CharField(_('property'), max_length=100)
class UniteProperty2(models.Model):
name = models.ForeignKey(Material, verbose_name=_('name'))
nature_unit = models.ForeignKey(Property, verbose_name=_('category'))
choix = models.ForeignKey(Physic, verbose_name=_('properties'), null=True, blank=True, related_name='choix')
What I want to do is when I am selecting nature_unit in UniteProperty2 it shows only properties (in administration interface)
that belong to the current category '
(ex: if i select Mechanical, i will have 'Hardness vickers', 'young Modulus', Maximum stress' in the dropdown list 'choix')
in the admin.py file, I have
class UniteProperty2InlineForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(UniteProperty2InlineForm, self).__init__(*args, **kwargs)
self.fields['choix'].queryset = Physic.objects.filter(
name=self.instance.name)
class UniteProperty2Inline(admin.TabularInline):
model = UniteProperty2
form = UniteProperty2InlineForm
class MaterialAdmin(admin.ModelAdmin):
inlines = (UniteProperty2Inline, ..)
but it doesn't work...i have the error 'No exception supplied' Exception Type: DoesNotExist
the problem is : name__name=self.instance.name

self.instance.name - in ur form refers to UniteProperty2 name - which is a Material obj
so try this
self.fields['choix'].queryset = Physic.objects.filter(
name=self.instance.choix )
or for all options:
self.fields['choix'].queryset = Physic.objects.filter(
name=self.instance.choix )

Related

"<VariationPrice>" needs to have a value for field "id" before this many-to-many relationship can be used

I have this model in my django code. I want to check whether VariationPrice for a product with the variations exist. If it exists, make changes to the already existing VariationPrice else save the new VariationPrice.
The error I'm getting on saving new VariationPrice is
"<VariationPrice: >" needs to have a value for field "id" before this many-to-many relationship can be used.
The code I have in models.py is:
class Product(models.Model):
....
product_name = models.CharField(max_length=200, unique=True)
class Variation(models.Model):
....
variation_value = models.CharField(max_length=100)
class VariationPrice(models.Model):
price = models.IntegerField()
product = models.ForeignKey(Product, on_delete=models.CASCADE)
variations = models.ManyToManyField(Variation, blank=True)
def __str__(self):
if self.pk is not None:
return ", ".join(str(var) for var in self.variations.all())
else:
return ''
def save(self, *args, **kwargs):
if self.pk is None:
var_list = [i['variation_value'] for i in self.variations.values()]
vprice = VariationPrice.objects.filter(product=product,variations__variation_value__in=var_list).annotate(num_attr=Count('variations__variation_value')).filter(num_attr=len(var_list))
if vprice.exists():
self.pk = vprice.first().pk
super(VariationPrice, self).save(*args, **kwargs)

Attribute Error: Object has no Attributes while trying to update the manytoo

Attribute Error: Object has no Attributes "avg_rating"
Errors occurs while trying to update the ManytooOne Field in views.py perform_create func.
Imm trying to update a avg_rating which is instance of Review model, Every time an a comment is received. my review is related to watchlist through foreign key.
Here's my models.py
class WatchList(models.Model):
title = models.CharField(max_length=50)
storyline = models.CharField(max_length=200)
platform = models.ForeignKey(StreamPlatform, on_delete=models.CASCADE, related_name="watchlist")
active = models.BooleanField(default=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
class Review(models.Model):
username = models.ForeignKey(User, on_delete=models.CASCADE)
rating = models.PositiveIntegerField(validators = [MinValueValidator(1), MaxValueValidator(5)])
comment = models.CharField(max_length=200, null=True)
watchlist = models.ForeignKey(WatchList, on_delete=models.CASCADE, related_name="reviews")
avg_rating = models.FloatField(default=0)
num_rating = models.IntegerField(default=0)
active = models.BooleanField(default=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
def __str__(self):
return self.comment
serializer.py
class ReviewSerializer(serializers.ModelSerializer):
username = serializers.StringRelatedField(read_only=True)
class Meta:
model = Review
exclude = ["watchlist"]
class WatchListSerializer(serializers.ModelSerializer):
### using relation serializer to list all the reviews in a movie
reviews = ReviewSerializer(many=True, read_only=True)
class Meta:
model = WatchList
fields = "__all__"
views.py
class ReviewCreateView(generics.CreateAPIView):
serializer_class = ReviewSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
return Review.objects.all()
def perform_create(self, serializer):
##posting the comment in a movie using movies primary key
primary_key = self.kwargs.get("pk")
watchlist = WatchList.objects.get(pk=primary_key)
#NOt allowing the same user to comment on the same movie twice
username = self.request.user
review_queryset = Review.objects.filter(watchlist=watchlist, username=username)
if review_queryset.exists():
raise ValidationError("You cannot comment on the same movie Twice")
##NOTE: want to updated this section
if watchlist.avg_rating == 0:
watchlist.avg_rating = serializer.validated_data['avg_rating']
else:
watchlist.avg_rating = (watchlist.avg_rating + serializer.validated_data['avg_rating'])/2
watchlist.num_rating += 1
watchlist.avg_rating.save()
serializer.save(watchlist=watchlist, username=username)

Override the variable from parent class in Django Models

I have a model ChildModel and it has two parent classes ActivatorModel and TimeStampedModel
Below are the three models:
The two base classes
class ActivatorModel(models.Model):
"""
ActivatorModel
An abstract base class model that provides activate and deactivate fields.
"""
INACTIVE_STATUS = 0
ACTIVE_STATUS = 1
STATUS_CHOICES = (
(INACTIVE_STATUS, _('Inactive')),
(ACTIVE_STATUS, _('Active')),
)
status = models.IntegerField(_('status'), choices=STATUS_CHOICES, default=ACTIVE_STATUS)
activate_date = models.DateTimeField(blank=True, null=True, help_text=_('keep empty for an immediate activation'))
deactivate_date = models.DateTimeField(blank=True, null=True, help_text=_('keep empty for indefinite activation'))
objects = ActivatorModelManager()
class Meta:
ordering = ('status', '-activate_date',)
abstract = True
def save(self, *args, **kwargs):
if not self.activate_date:
self.activate_date = now()
super().save(*args, **kwargs)
class TimeStampedModel(models.Model):
"""
TimeStampedModel
An abstract base class model that provides self-managed "created" and
"modified" fields.
"""
created = CreationDateTimeField(_('created'))
modified = ModificationDateTimeField(_('modified'))
def save(self, **kwargs):
self.update_modified = kwargs.pop('update_modified', getattr(self, 'update_modified', True))
super().save(**kwargs)
class Meta:
get_latest_by = 'modified'
abstract = True
The class using the above base models
class ChildModel(ActivatorModel, TimeStampedModel):
child_id = models.CharField(
max_length=36, primary_key=True, default=uuid.uuid4
)
display_name = models.CharField(blank=True, max_length=100, null=True)
status = models.CharField(max_length=20, null=True, blank=True, default='Active')
The issues is whenever I try to save some character type value in ChildModel.status, it throws an error
invalid input syntax for integer.
How to properly override the variable status and have it the properties defined in the ChildModel?

Django search with AND operator many to many relationship

This is my model
class MenuItem(models.Model):
name = models.CharField(max_length=500, null=False)
description = models.CharField(max_length=500, null=True)
image_url = models.CharField(max_length=1000, null=True)
menu_category = models.ForeignKey(MenuCategory, on_delete=models.CASCADE)
def __str__(self):
return f'{self.name}'
class Venue(models.Model):
name = models.CharField(max_length=500, null=False)
def __str__(self):
return f'{self.name}'
class VenueMenu(models.Model):
venue = models.ForeignKey(Venue, null=False, on_delete=models.CASCADE)
menu_item = models.ManyToManyField(MenuItem, null=False)
This is my view
#api_view(['GET'])
def search_menu_item(request):
if request.GET.get('venue') and request.GET.get('search_name'):
menu_item_filter = Q(menu_item__name__icontains=request.GET.get('search_name'))
venue_filter = Q(venue__name=request.GET.get('venue').title())
menu_item_search = VenueMenu.objects.filter(venue_filter & menu_item_filter)
serializer = VenueMenuSerializer(menu_item_search, many=True)
return Response(serializer.data)
This is my serializer
class MenuItemSerializer(serializers.ModelSerializer):
menu_category = MenuCategorySerializer(many=True)
class Meta:
model = MenuItem
fields = '__all__'
class VenueSerializer(serializers.ModelSerializer):
class Meta:
model = Venue
fields = '__all__'
class VenueMenuSerializer(serializers.ModelSerializer):
menu_item = MenuItemSerializer(many=True)
venue = VenueSerializer(many=False)
I want to search a single menu item information in a specific venue, as you can see in my query am querying the object but this query is returning me all the menu items associated with that venue including the one which I have searched but I want to have a single menu item which I am searching associated to that venue.
You get the correctly filtered VenueMenus, but the related MenuItems aren't filtered automatically because the filter is for VenueMenu and not MenuItem.
To filter the related MenuItems in the serializer, you'll have to do a filtered prefetch using Prefetch like so:
from django.db.models import Prefetch
menu_item_search = VenueMenu.objects.filter(
venue_filter & menu_item_filter
).prefetch_related(
Prefetch(
'menu_item',
queryset=MenuItem.objects.filter(name__icontains=request.GET.get('search_name'))
)
)
Also to improve this further, you can also select the related Venue using select_related to avoid doing a separate query just to get the venue details in the serializer. So all in all:
menu_item_search = VenueMenu.objects.filter(
venue_filter & menu_item_filter,
).select_related(
'venue',
).prefetch_related(
Prefetch(
'menu_item',
queryset=MenuItem.objects.filter(name__icontains=request.GET.get('search_name'))
)
)

How to set different formfield_for_foreignkey option list for different form like add and change

I defined a model named Emp (employees) as below:
from django.db import models
from django.contrib.auth.models import User
class Emp(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
empid = models.PositiveIntegerField(unique=True)
cnname = models.CharField(max_length=20)
gender = models.BooleanField(default=True)
birthdate = models.DateField(default=None, blank=True, null=True)
joindate = models.DateField()
islogi = models.BooleanField(default=True)
isleave = models.BooleanField(default=False)
leavedate = models.DateField(default=None, blank=True, null=True)
In Admin.py I have following code:
from django.contrib import admin
from django.contrib.auth import get_user_model
from .models import Emp
class EmpAdmin(admin.ModelAdmin):
list_display = ('id','empid','cnname','gender','birthdate','joindate',
'islogi','isleave','leavedate')
ordering = ('empid',)
search_fields = ['empid','cnname']
list_per_page = 12
exclude = ('isleave','leavedate',)
# for list view, if not admin just show emp self info
# for admin all employees will be listed.
def get_queryset(self, request):
qs = super().get_queryset(request)
if request.user.is_superuser:
return qs
else:
user = get_user_model()
return qs.filter(empid=Emp.objects.get(user_id=user.objects.get(username=request.user).id).empid)
# this is used for form add and change actions
# but I want to add and change have different option list
# add just list the users which are not employees
# change list the users which are not employees + employee self
def formfield_for_foreignkey(self, db_field, request, **kwargs):
print("self", self)
print("db_field", db_field)
print("build_absolute_uri() => ", request.build_absolute_uri())
if db_field.name == 'user':
print("kwargs => ", kwargs)
users = get_user_model()
u = users.objects.all()
uidlist = []
for x in range(len(u)):
uidlist.append(u[x].id)
e = Emp.objects.all()
for x in range(len(e)):
uidlist.remove(e[x].user_id)
kwargs["queryset"] = users.objects.filter(id__in=uidlist)
return super().formfield_for_foreignkey(db_field, request, **kwargs)
admin.site.register(Emp, EmpAdmin)
Above "formfield_for_foreignkey" it list the same option list for "user" option list. How can I override the list for "add" and "change" actions, so they can use different option lists?
You could use the request that is passed in to check if the request is a change or add:
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if "/add/" in request.path:
#add logic
elif "/change/" in request.path:
#change logic

Resources