In my app, there's a list of categories and subcategories with a ForeignKey relationship. Say, there're:
Subcategory1 related to Category1
Subcategory2 related to Category2
I expect to get the following subcategory urls:
http://127.0.0.1:8000/cat1/subcat1
http://127.0.0.1:8000/cat2/subcat2
These urls work fine. However, django also generates these urls that I don't need:
http://127.0.0.1:8000/cat1/subcat2
http://127.0.0.1:8000/cat2/subcat1
Why do they appear in my app? How do I get rid of them? Thanks in advance!
models.py:
class Category(models.Model):
categoryslug = models.SlugField(max_length=200, default="",unique=True)
def get_absolute_url(self):
return reverse("showrooms_by_category",kwargs={'categoryslug': str(self.categoryslug)})
class Subcategory(models.Model):
subcategoryslug = models.SlugField(max_length=200, default="",unique=True)
category = models.ForeignKey('Category', related_name='subcategories',
null=True, blank=True, on_delete = models.CASCADE)
def get_absolute_url(self):
return reverse("showrooms_by_subcategory",
kwargs={'categoryslug': str(self.category.categoryslug), 'subcategoryslug': str(self.subcategoryslug)})
views.py:
class ShowroomCategoryView(DetailView):
model = Category
context_object_name = 'showrooms_by_category'
template_name = "website/category.html"
slug_field = 'categoryslug'
slug_url_kwarg = 'categoryslug'
class ShowroomSubcategoryView(DetailView):
model = Subcategory
context_object_name = 'showrooms_by_subcategory'
template_name = "website/subcategory.html"
slug_field = 'subcategoryslug'
slug_url_kwarg = 'subcategoryslug'
urls.py:
urlpatterns = [
path('<slug:categoryslug>/<slug:subcategoryslug>/', views.ShowroomSubcategoryView.as_view(), name='showrooms_by_subcategory'),
path('<slug:categoryslug>/', views.ShowroomCategoryView.as_view(), name='showrooms_by_category'),
]
I think the reason for this is foreign_key. So, I think as you can use one to one field to get the target, like:
subcategoryslug = models.SlugField(max_length=200, default="",unique=True)
category = models.OneToOneField('Category', related_name='subcategories',null=True, blank=True, on_delete = models.CASCADE)
*Note:- Please understand the logic too behind it. For that, do research more.
Related
I am complete beginner and I made a django project a while ago. Its main purpose is adding restaurants and categories and food menu respectively. For now, it is working well, if I go to the required restaurant, it shows its categories. But now, I want to improve it via Vuejs and that's why I'm trying to make Django REST API. But I can't filter categories and food according to their restaurant, because category model is not directly connected to restaurant model. My models.py is:
from django.db import models
from django.conf import settings
class Restaurant(models.Model):
owner = models.OneToOneField(settings.AUTH_USER_MODEL, related_name="restaurant", on_delete=models.CASCADE)
name = models.CharField(max_length=256, db_index=True)
slug = models.SlugField(max_length=256, unique=True)
logo = models.ImageField(upload_to='logos/', blank=True)
def __str__(self):
return str(self.name)
class Category(models.Model):
name = models.CharField(max_length=256, db_index=True)
slug = models.SlugField(max_length=256, unique=True)
icon = models.ImageField(upload_to='categories/', blank=True)
class Meta:
verbose_name = 'category'
verbose_name_plural = 'categories'
def __str__(self):
return str(self.name)
class Food(models.Model):
restaurant = models.ForeignKey(Restaurant, related_name='foods', on_delete=models.CASCADE)
category = models.ForeignKey(Category, related_name='foods', on_delete=models.CASCADE)
name = models.CharField(max_length=256, db_index=True)
slug = models.SlugField(max_length=256, blank=True)
price = models.IntegerField(default=0)
description = models.TextField(blank=True)
image = models.ImageField(upload_to='foods/', blank=True)
available = models.BooleanField(default=True)
def __str__(self):
return str(self.name)
As you see, category class is not directly connected to restaurant. It is filtered with views.py. My views.py is:
from django.shortcuts import render, get_object_or_404
from .models import Restaurant, Category, Food
def food_list(request, restaurant_slug, category_slug):
restaurant = get_object_or_404(Restaurant, slug=restaurant_slug)
category = get_object_or_404(Category, slug=category_slug)
foods = Food.objects.filter(restaurant=restaurant, category=category, available=True)
categories = set(list(Category.objects.filter(foods__restaurant=restaurant)))
return render(request, 'restaurant/food_list.html', {'restaurant': restaurant, 'category': category, 'foods': foods, 'categories': categories})
def category_list(request, restaurant_slug=None):
restaurant = get_object_or_404(Restaurant, slug=restaurant_slug)
categories = set(list(Category.objects.filter(foods__restaurant=restaurant)))
return render(request, 'restaurant/category_list.html', {'restaurant': restaurant, 'categories': categories})
As I mentioned above it is working well now. But I want to GET categories and food respect to their restaurants. How to filter it with Django REST API?
Example code of filtering in Django :
class PassengerList(generics.ListCreateAPIView):
model = Passenger
serializer_class = PassengerSerializer
# Show all of the PASSENGERS in particular WORKSPACE
# or all of the PASSENGERS in particular AIRLINE
def get_queryset(self):
queryset = Passenger.objects.all()
workspace = self.request.query_params.get('workspace')
airline = self.request.query_params.get('airline')
if workspace:
queryset = queryset.filter(workspace_id=workspace)
elif airline:
queryset = queryset.filter(workspace__airline_id=airline)
return queryset
#store/admin.py
#admin.register(models.Product)
class ProductView(admin.ModelAdmin):
list_display = ['title', 'unit_price', 'inventory_status', 'collection_title',]
list_editable = ['unit_price']
list_per_page = 10
list_select_related = ['collection']
#store/models.py
class Collection(models.Model):
title = models.CharField(max_length=255)
featured_product = models.ForeignKey(
'Product', on_delete=models.SET_NULL, null=True, related_name='+')
class Meta:
ordering = ['title']
'''
def __str__(self) -> str:
return self.title
'''
class Product(models.Model):
title = models.CharField(max_length=255)
slug = models.SlugField()
description = models.TextField()
unit_price = models.DecimalField(max_digits=6, decimal_places=2)
inventory = models.IntegerField()
last_update = models.DateTimeField(auto_now=True)
collection = models.ForeignKey(Collection, on_delete=models.PROTECT)
promotions = models.ManyToManyField(Promotion)
#Error I got:
SystemCheckError
PS - I know I can use 'collection' in list_display directly as it is already a field in my product model, but I want to preload a related field/table using list_select_related and use 'collection_title' in list_display. Please help. Thank You.
ERRORS:
<class 'store.admin.ProductView'>: (admin.E108) The value of 'list_display[3]' refers to 'col
lection_title', which is not a callable, an attribute of 'ProductView', or an attribute or method on 'store.Product'.
I think you're wrong, as far as I know list_select_related is to reduce your query, but your error is something else, here it says "list_display [3]" referred to colection_title, which is not a attribute or callable method of model
Product or ProductView class.
django validate this items(list_display) In the following link, you can see the validate function(_check_list_display):
https://github.com/django/django/blob/950d697b95e66deb3155896e0b619859693bc8c6/django/contrib/admin/checks.py#L732
If you want to access the related field in other models, you can create a function in your ProductView link this:
class ProductView(admin.ModelAdmin):
list_display = ['title', 'unit_price', 'inventory_status', 'collection_title',]
list_editable = ['unit_price']
list_per_page = 10
list_select_related = ['collection']
def collection_title(self, obj):
return obj.collection.title
I want to display product detail page using drf but I keep running into one error after another.
urls.py
path('product/<int:id>', views.product_detail_view.as_view(), name='product-detail'),
models.py
class Product(models.Model):
categories = models.ManyToManyField(Category)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="product_owner")
item = models.CharField(max_length=150)
slug = models.SlugField(max_length=255, blank=True, null=True)
brand = models.CharField(max_length=255, default="brand")
image = models.ImageField(upload_to="images/products/")
label = models.CharField(max_length=254, default='', blank=True, null=True)
serializers.py
class product_detail_serializer(serializers.ModelSerializer):
category = serializers.SerializerMethodField()
class Meta:
model = Product
fields = ("id", "categories", "item", "slug", "image")
lookup_field = "id"
def get_category(self, obj):
return obj.get_category_display()
views.py
class product_detail_view(generics.RetrieveAPIView):
serializer_class = product_detail_serializer
lookup_field = "id"
The error I'm getting now is 'Product has no attribute 'get_category_display'
Please how do I fix this error?
You can try defining the id inside the product_detail_serializer class then include it in the url patterns path like this:
serializers.py
class product_detail_serializer(serializers.ModelSerializer):
category = serializers.SerializerMethodField()
id = serializers.IntegerField(read_only=True)
Then include the id in the urlpatherns path
path('product/<int:id>',views.product_detail_view.as_view(),name='product
detail'),
I basically can't explain why this worked. I used the serializer for product instead of creating another serializer for the product detail page.
I'm trying to make it so that I can show only briefs with the slug name as the category, however it does not work.
At the minute I can only use it by showing all briefs using .objects.all() however this is not suitable for my desired use case.
Do i need a slug field in the brief section too?
Models.py
class Category(models.Model):
name = models.CharField(max_length=200, unique=True)
slug = models.SlugField(max_length=50, unique=True)
class Meta:
verbose_name_plural = 'categories'
verbose_name = 'category'
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('browse')
class Brief(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
brandname = models.CharField(max_length=28)
description = models.CharField(max_length=200)
date = models.DateTimeField(auto_now=True, blank=True)
category = models.ForeignKey(Category, on_delete=CASCADE)
def get_absolute_url(self):
return reverse('homepage')
Urls.py
path('browse/categories/<slug:catslug>/', views.postsinthecategory, name = 'catslug'
views.py
def postsinthecategory(request, catslug):
categories = Category.objects.all()
brief = Brief.objects.all()
if catslug:
category = get_object_or_404(Category, slug = catslug)
brief = Brief.objects.get(category=catslug)
template = 'users/categoryposts.html'
context = {'categories': categories, 'brief': brief, 'category': category}
return render(request, template,context)
You can use __slug to filter on the related model's slug field. Use filter instead of get, because you want a queryset that can contain more than brief. I would rename brief to briefs in your view to make that clearer.
briefs = Brief.objects.filter(category__slug=catslug)
Or, since you fetched the category on the previous line, you could do:
category = get_object_or_404(Category, slug = catslug)
briefs = Brief.objects.filter(category=category)
I want to design a url like :
url(r'^theaters/(?P<area>.+)/(?P<title>.+)/$', TheaterAreaList.as_view(), name='theater-area'),
and then I can go to the link like:
http://127.0.0.1:8000/theaters/north/moviename
But the title and area are in different models with manytomany relationship :
models.py
class Movie(models.Model):
link = models.URLField()
title = models.CharField(max_length=255, null=True)
class MovieTheater(models.Model):
movietheater = models.ManyToManyField(Movie,null=True,blank=True,through="MovieShowtime")
movie_theater = models.CharField(max_length=255, null=True)
city = models.CharField(max_length=255, null=True) #east west north south
class MovieShowtime(models.Model):
theater = models.ForeignKey( MovieTheater, null=True,blank=True,related_name = 'theater' )
movie = models.ForeignKey( Movie, null=True,blank=True,related_name = 'movie' )
time = models.TextField(null=True,blank=True)
my views.py has error QuerySet' object has no attribute 'movietheater_set'
views.py:
class TheaterAreaList(generics.ListAPIView):
serializer_class = TheaterSerializer
def get_queryset(self):
area = self.kwargs['city']
title = self.kwargs['title']
return MovieTheater.objects.filter(city=area).movietheater_set.filter(title=title)
Please teach me how to query with two models ,Thank you very much.
The _set would work only for objects, and not filtered querysets.
Try this:
MovieTheater.objects.filter(city=area, movietheater__movie__title=title)
Here is the relevant documentation