Filtering in Django ModelViewSet - python

I don't think I'm doing this correctly. What I'm trying to do is prevent data from being returned if it doesn't match my criteria. Here are the criteria:
User should be able to see the project if:
If the object is private and they are the owner
-- OR --
The project is not private
If the user is the owner, or they are a member of the assigned group
EDITED PER THE COMMENT:
The code below seems to work, but I'm still not sure if this is the correct logic or the right way to do this.
api.py
class ProjectListViewSet(viewsets.ModelViewSet):
queryset = Project.objects.all().order_by('name')
serializer_class = ProjectLightSerializer
authentication_classes = [TokenAuthentication, ]
permission_classes = [IsAuthenticated, ]
def list(self, request, *args, **kwargs):
projects = []
queryset = super().get_queryset()
is_private = queryset.values('is_private')
owner = queryset.filter(owner__exact=self.request.user)
if not self.request.user.is_superuser:
if is_private and not owner:
return Response(data=[])
elif is_private and owner:
projects = queryset.filter(groups__in=self.request.user.groups.all())
else:
projects = super().get_queryset()
projects = self.filter_queryset(projects)
serializer = self.get_serializer(projects, many=True)
result_set = serializer.data
return Response(result_set)
serializers.py
class ProjectLightSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField()
slug = serializers.CharField()
description = serializers.CharField()
groups = GroupSerializer(many=True)
created_date = serializers.DateTimeField()
modified_date = serializers.DateTimeField()
owner = UserSerializer()
is_private = serializers.BooleanField()
models.py
class Project(models.Model):
name = models.CharField(max_length=60, help_text=_("Name of project"))
slug = models.SlugField(default="")
groups = models.ManyToManyField(Group, help_text=_("Attached workgroup"))
description = models.TextField(null=False, blank=False,
verbose_name=_("description"))
logo = models.FileField(upload_to=get_project_logo_file_path,
max_length=500, null=True, blank=True,
verbose_name=_("logo"))
created_date = models.DateTimeField(null=False, blank=False, verbose_name=_("created date"), auto_now_add=True)
modified_date = models.DateTimeField(null=False, blank=False, auto_now_add=True, verbose_name=_("modified date"))
owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True,
related_name="owned_projects", verbose_name=_("owner"), on_delete=models.CASCADE)
is_private = models.BooleanField(default=True, null=False, blank=True,
verbose_name=_("is private"))
tags = TaggableManager(help_text=_('A comma-separated list of tags.'), blank=True, through=TaggedItem,
to=Tag, verbose_name='Tags')
def __str__(self):
return self.name
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.name)[:255]
while type(self).objects.filter(slug=self.slug).exists():
match_obj = re.match(r'^(.*)-(\d+)$', self.slug)
if match_obj:
next_int = int(match_obj.group(2)) + 1
self.slug = match_obj.group(1) + "-" + str(next_int)
else:
self.slug += '-2'
super(Project, self).save(*args, **kwargs)
def get_tags_display(self):
return self.tags.values_list('name', flat=True)
class Meta:
db_table = 'projects'
ordering = ["name", "id"]
verbose_name = "Project"
verbose_name_plural = "Projects"
Any assistance would be greatly appreciated. Thanks in advance.

You can override the get_queryset method instead of list
from django.db.models import Q
class ProjectListViewSet(viewsets.ModelViewSet):
serializer_class = ProjectLightSerializer
authentication_classes = [TokenAuthentication, ]
permission_classes = [unauthenticated, ]
def get_queryset(self, request, *args, **kwargs):
queryset = super().get_queryset(self, request, *args, **kwargs)
is_private_query = Q(is_private=True, owner=self.request.user)
groups_user_is_part_of = self.request.user.groups().values_list('id', flat=True)
is_not_private_query = Q(is_private=False) & (Q(owner=self.request.user) | Q(groups__id__in=groups_user_is_part_of))
return queryset.filter(is_private_query | is_not_private_query).order_by('name')
More here https://docs.djangoproject.com/en/3.0/topics/db/queries/#complex-lookups-with-q-objects

Related

blog objects need to have a primary key value before you can access their tags

i am trying to create a blog by the admin panell. but i am not able to save. Can you please help.
Model.py
class blog(models.Model):
author = models.ForeignKey(User, null=True, on_delete=models.CASCADE)
city_id = models.AutoField(primary_key=True)
blog_title=models.CharField(max_length=200)
created_at = models.DateTimeField(auto_now_add=True)
slug = models.CharField(max_length=500, blank=True)
tags = TaggableManager()
blog_category_name=models.ForeignKey(blog_category,on_delete=models.CASCADE,null=True,blank=True)
blog_sub_category_name=models.ForeignKey(blog_sub_category,on_delete=models.CASCADE,null=True,blank=True)
written_by = models.CharField(max_length=200, default='sandeep')
image_banner= models.ImageField(upload_to='image_banner')
medium_thumbnail = models.ImageField(upload_to='medium_thumbnail')
content = RichTextField() # RichTextField is used for paragraphs
is_authentic=models.BooleanField(default=False)
class Meta: # Plurizing the class name explicitly
verbose_name_plural = 'blog'
def __str__(self): # Dundar Method
return self.blog_title
def save(self, *args, **kwargs): # Saving Modefied Changes
if not self.slug:
self.slug = slugify(self.blog_title)
view.py
def tags(request,tags):
blogs = blog.objects.filter(
Q(blog_title__icontains = tags.replace('-',' ')) |
Q(content__icontains = tags.replace('-',' '))
)
print(tags)
return render(request, 'services/blog-with-sidebar.html',
{'blogs': blogs,'media_url':settings.MEDIA_URL})
For some reason when I'm trying to save the tags and the data I'm getting this error:

Related Field got invalid lookup: name

I am trying to implement Q search on my APIView, but it says invalid lookups name which is strange. I have added the search fields according to the fields of the models.
My view:
from django.db.models import Q
class PrdouctSearchAPIView(ListAPIView):
permission_classes = [AllowAny]
# def list(self, request, *args, **kwargs):
def get(self, request, *args, **kwargs):
qur = self.request.query_params.get('search')
item = Product.objects.filter(Q(category__name__icontains=qur)|
Q(brand__name__icontains=qur)|
Q(description__icontains=qur)|
Q(collection__name__icontains=qur)|
Q(variants__name__icontains=qur))
serializer = ProductSerializer(item,many=True)
return Response(serializer.data)
My models:
class Product(models.Model):
merchant = models.ForeignKey(Seller,on_delete=models.CASCADE,blank=True,null=True)
category = models.ManyToManyField(Category, blank=False)
sub_category = models.ForeignKey(Subcategory, on_delete=models.CASCADE,blank=True,null=True)
brand = models.ForeignKey(Brand, on_delete=models.CASCADE)
collection = models.ForeignKey(Collection, on_delete=models.CASCADE)
featured = models.BooleanField(default=False) # is product featured?
description = RichTextField(blank=True)
variants = models.ManyToManyField(Variants,related_name='products')
class Category(models.Model):
#parent = models.ForeignKey('self',related_name='children',on_delete=models.CASCADE,blank=True,null=True)
name = models.CharField(max_length=100, unique=True)
image = models.ImageField(null=True, blank=True)
class Brand(models.Model):
brand_category = models.ManyToManyField(Category,blank=True,null=True)
name = models.CharField(max_length=100, unique=True)
class Collection(models.Model):
name = models.CharField(max_length=100, unique=True)
image = models.ImageField(null=True, blank=True)
My url is :
path('api/productsearch',views.PrdouctSearchAPIView.as_view(),name='api-productsearch'),
As we can see there are fields "category__name" and such not only "name", but the error says invalid lookup "name".

Django admin - How to add request.user as default vaule for a Inline model?

I've written a small support application made out of 2 models, please see below.
As I answere onto support tickets at the django admin panel, I dont always want to set the author name manully for each reply I add to the ticket. Instead I want that request.user I set as author automatically as initial vaule. Sadly I was not able to find any solution at the django docs that seem to solve this issue ...
admin.py
class SupportTicketRepliesInline(admin.TabularInline):
model = SupportTicketReplies
extra = 0
min_num = 1
class SupportTicketsAdmin(admin.ModelAdmin):
list_display = ['requester', 'creation_date', 'status', 'category', 'subject']
ordering = ['-creation_date']
list_filter = ['creation_date']
inlines = [
SupportTicketRepliesInline,
]
admin.site.register(SupportTickets, SupportTicketsAdmin)
models.py
...
class SupportTickets(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique=True)
requester = models.ForeignKey(User, on_delete=models.CASCADE, null=False, blank=False)
category = models.IntegerField(choices=TICKET_CATEGORY, verbose_name='Ticket Category')
subject = models.CharField(max_length=30)
description = models.TextField(max_length=2000, blank=False, verbose_name='Problem description')
status = models.IntegerField(choices=STATUS_OF_TICKET, verbose_name='Ticket Status', default=2)
creation_date = models.DateTimeField(auto_now_add=True, blank=False)
def publish(self):
self.creation_date = timezone.now()
self.save()
class Meta:
verbose_name = "Support Ticket"
verbose_name_plural = "Support Tickets"
ordering = ['-creation_date']
models.py
class SupportTicketReplies(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
ticket = ForeignKey(SupportTickets, related_name='replies', on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='Author', blank=True)
content = models.TextField(verbose_name="Content", max_length=2000)
creation_date = models.DateTimeField(auto_now_add=True, blank=False)
class Meta:
verbose_name = "Ticket Reply"
verbose_name_plural = "Ticket Replies"
ordering = ['creation_date']
i think everything what you need is :
https://docs.djangoproject.com/en/3.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.formfield_for_foreignkey
it will look smrh like:
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == "author":
self.fields['author'].queryset = UserModel.objects.filter(id=request.usermodel.id)
also you should remove blank=True from your model.
or you can just add to your inline class as initial value:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['author'].initial = self.request.user
The following works for me:
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == "author":
author = request.user
if author:
kwargs['initial'] = author.pk
return super(SupportTicketRepliesInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
At a admin.TabularInline or admin.StackedInline

Can't get Django REST to save POST object request in model

Trying to allow users to create an object and save it in my model. My front-end POST requests are getting a 201 confirmation response, so no traceback error from the backend. The data seems to be getting serialized but not saved in my model.
Here is my code...
model.py:
class Bucket(models.Model):
category_options = (
('personal', 'Personal'),
('social', 'Social'),
)
class BucketObjects(models.Manager):
def get_queryset(self):
return super().get_queryset()
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='buckets')
admin_user = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='admin_user')
guest_user = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='guest_user', blank=True)
category = models.CharField(max_length=30, choices=category_options)
name = models.CharField(max_length=35)
created = models.DateTimeField(default=timezone.now, blank=True)
slug = AutoSlugField(populate_from = "random_string", blank=True)
stock_count = models.IntegerField(blank=True, null=True)
stock_list = ArrayField(models.CharField(max_length=6,null=True),size=30,null=True, blank=True)
about = models.CharField(max_length=200)
objects = models.Manager()
bucketobjects = BucketObjects()
class Meta:
ordering = ('-created',)
def save(self, *args, **kwargs):
if self.stock_list:
self.stock_count = len(self.stock_list)
super().save(*args, **kwargs)
serializer.py:
class BucketCreateSerializer(serializers.ModelSerializer):
class Meta:
model = Bucket
fields = ('owner','category','name','about')
read_only_fields = ['owner']
def create(self, validated_data):
user = self.context['request'].user
bucket = Bucket.objects.create(
owner=user,
**validated_data
)
bucket.save()
return bucket
view.py:
class CreateBucket(generics.CreateAPIView):
permission_classes = [IsAuthenticated]
serializer_class = BucketCreateSerializer
queryset = Bucket.objects.all()
How can I go about solving this?

Django : Edit the many to many field Inline using Django Admin

I want to edit the cuisines associated with a dish on the dish panel in django admin. I am trying to use InlineModelAdmin. The problem is that I can edit the DishCuisinesMap objects but not the cuisines object.
Here is the screenshot:
This is my models.py:
class Cuisines(models.Model):
cuisine_id = models.IntegerField(primary_key=True)
cuisine_mtom = models.ManyToManyField('DishInfo', through='DishCuisinesMap')
name = models.TextField()
cuisine_sf_name = models.TextField()
type = models.IntegerField()
insert_time = models.DateTimeField()
update_time = models.DateTimeField()
class Meta:
managed = False
db_table = 'cuisines'
class DishCuisinesMap(models.Model):
dish = models.ForeignKey('DishInfo', on_delete=models.PROTECT,
related_name='dcm_dish')
cuisine = models.ForeignKey(Cuisines, on_delete=models.PROTECT,
related_name='dcm_cuisine')
insert_time = models.DateTimeField()
update_time = models.DateTimeField()
class Meta:
managed = False
db_table = 'dish_cuisines_map'
unique_together = (('dish', 'cuisine'),)
class DishInfo(models.Model):
dish_id = models.BigIntegerField(primary_key=True)
bid = models.ForeignKey(Brands, on_delete=models.PROTECT, db_column='bid',
related_name='dinfo_brand')
name = models.TextField()
is_live = models.IntegerField()
veg_nonveg_ind = models.SmallIntegerField(blank=True, null=True)
sf_name = models.TextField()
descr = models.TextField(blank=True, null=True)
expert_tag = models.TextField(blank=True, null=True)
special_desc = models.TextField(blank=True, null=True)
reco_percent = models.IntegerField(blank=True, null=True)
one_liner = models.TextField(blank=True, null=True)
nutrition = JSONField()
insert_time = models.DateTimeField()
update_time = models.DateTimeField()
images = ArrayField(models.TextField())
class Meta:
managed = False
db_table = 'dish_info'
unique_together = (('bid', 'name'), ('bid', 'sf_name'),)
def __unicode__(self):
brand_name = self.bid.name
return self.name
and this is my admin.py:
class CityDishFilter(admin.SimpleListFilter):
title = ('city name')
parameter_name = 'city_id'
def lookups(self, request, model_admin):
list_of_cities = list()
queryset = Cities.objects.all()
for city in queryset:
list_of_cities.append(
(str(city.city_id), city.name)
)
return list_of_cities
def queryset(self, request, queryset):
query_params = request.GET
if self.value():
city_query = DelLocations.objects.filter(city__city_id=self.value())\
.values('del_id')
loc_query = DelLocationsMapping.objects.filter(del_loc__in=
city_query).values('plot')
dopm_query = DishOutletPlatMapping.objects.filter(plot__in
=loc_query).values('dish')
final_query = DishInfo.objects.filter(dish_id__in=dopm_query)
if 'veg_nonveg_ind' in query_params:
final_query = final_query.filter(veg_nonveg_ind=query_params.get('veg_nonveg_ind'))
if 'is_live' in query_params:
final_query = final_query.filter(is_live=query_params.get('is_live'))
return final_query
return queryset
class BrandDishFilter(admin.SimpleListFilter):
title = ('brand name')
parameter_name = 'brand_id'
def lookups(self, request, model_admin):
query_params = request.GET
list_of_brands = list()
if 'city_id' in query_params:
city_query = DelLocations.objects.filter(city__city_id=query_params.get('city_id'))\
.values('del_id')
loc_query = DelLocationsMapping.objects.filter(del_loc__in=
city_query).values('plot')
dopm_query = DishOutletPlatMapping.objects.filter(plot__in
=loc_query).values('dish')
dish_query = DishInfo.objects.filter(dish_id__in=dopm_query).\
values('bid').distinct()
brand_query = Brands.objects.filter(bid__in=dish_query).order_by('name')
for brand in brand_query:
list_of_brands.append(
(str(brand.bid), brand.name)
)
return list_of_brands
else:
brand_query = Brands.objects.all().order_by('name')
for brand in brand_query:
list_of_brands.append(
(str(brand.bid), brand.name)
)
return list_of_brands
def queryset(self, request, queryset):
if 'brand_id' in request.GET:
queryset = queryset.filter(bid=self.value())
return queryset
return queryset
class DishCuisinesInline(admin.TabularInline):
model = DishCuisinesMap
extra = 1
class CuisinesAdmin(admin.ModelAdmin):
inlines = [DishCuisinesInline,]
fields = ("name", "cuisine_sf_name", "type")
class DishInfoAdmin(admin.ModelAdmin):
list_select_related = ('bid',)
list_display = ('name', 'brand_name')
fieldsets = (
(None, {'fields': ('name', 'sf_name', 'is_live', 'veg_nonveg_ind',
'one_liner',
)
}
),
('Advanced options', {'classes': ('collapse',),
'fields': ('descr', 'images')
}
)
)
search_fields = ['name', 'bid__name']
list_filter = ('veg_nonveg_ind', 'is_live', CityDishFilter, BrandDishFilter) #, 'bid__name'
inlines = [DishTimingsInline, DishCuisinesInline]
# changing the size of the text fields:
formfield_overrides = {
models.TextField: {'widget': Textarea(
attrs = {'rows': 4,
'cols': 50})
},
}
def brand_name(self, obj):
return obj.bid.name
admin.site.register(DishInfo, DishInfoAdmin)
admin.site.register(Cuisines, CuisinesAdmin)
The django docs here say that membership objects can be edited from Person or the Group detail pages, but what is way to edit the group from the person details page?
In my case I need a way to edit the cuisines from the dish page , I am pretty new to django. Any help is greatly appreciated.

Categories