I got products app with ListView and DetailView. I need to implement slug with title of products on detail page with no app label in url. Such as in detail page it should say 'mysite.com/complete-product-title' instead of 'mysite.com/products/complete-product-title'. This detail page can help me to great deal for SEO.
Following is the code:
models.py:
class Product(models.Model):
title = models.CharField(max_length=500)
description = models.TextField(blank=True, null=True)
price = models.DecimalField(max_digits=20, decimal_places=2)
sku = models.CharField(null=True, max_length=100)
url = models.URLField(blank=True)
slug = models.SlugField(default='')
categories = models.ManyToManyField('Category', blank=True)
default = models.ForeignKey('Category', related_name='default_category', null=True, blank=True)
def __unicode__(self):
return self.title
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super().save(*args, **kwargs)
def get_absolute_url(self):
kwargs={"slug": self.slug}
return reverse('product_detail', kwargs=kwargs)
Views.py:
class ProductListView(ListView):
model = Product
paginate_by = 20
def get_context_data(self, *args, **kwargs):
context = super(ProductListView, self).get_context_data(*args, **kwargs)
return context
def get_queryset(self, *args, **kwargs):
query = super(ProductListView, self).get_queryset(*args, **kwargs)
return query
class ProductDetailView(DetailView):
model = Product
slug_field = 'slug'
Project urls.py:
url(r'^products/', include('products.urls')),
App urls.py:
url(r'^(?P<slug>[\w-]+)/$', ProductDetailView.as_view(), name='product_detail'),
I have implemented elasticsearch with haystack and for it I have to add haystack urls in project root urls.py like url(r'^$', include('haystack.urls'), name='search'),
My complete models.py for products app are
class Product(models.Model):
title = models.CharField(max_length=500)
description = models.TextField(blank=True, null=True)
price = models.DecimalField(max_digits=20, decimal_places=2)
sku = models.CharField(null=True, max_length=100)
url = models.URLField(blank=True)
slug = models.SlugField(unique=False)
categories = models.ManyToManyField('Category', blank=True)
default = models.ForeignKey('Category', related_name='default_category', null=True, blank=True)
def __unicode__(self):
return self.title
def get_absolute_url(self):
return reverse('product_detail', kwargs={"slug":self.slug})
def get_image_url(self):
img = self.productimage_set.first()
if img:
return img.image.url
return img
class Category(models.Model):
title = models.CharField(max_length=120, unique=True)
#slug = models.SlugField(unique=True)
description = models.TextField(null=True, blank=True)
timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
def __unicode__(self):
return self.title
def image_upload_to(instance, filename):
title = instance.product.title
slug = slugify(title)
basename, file_extension = filename.split('.')
new_filename = '%s-%s.%s'%(basename, instance.id, file_extension)
return 'products/%s/%s' %(slug, new_filename)
class ProductImage(models.Model):
product = models.ForeignKey(Product)
image = models.ImageField(upload_to=image_upload_to)
def __unicode__(self):
return self.product.title
When I try to add slug=models.SlugField(unique=True) I get error message like column slug is not unique.
Now I can't remove products/ from url(r'^products/', include('products.urls')), because then it will make haystack urls and product app urls both default. Ultimate goal here is to just implement haystack urls as default but not showing 'mysite.com/products/product-title-as-slug' instead 'mysite.com/product-title-as-slug'.
Please assist how can I achieve this goal.
You can do this immediately by changing:
url(r'^products/', include('products.urls')),
to
url(r'^', include('products.urls')),
This is all you would need to do if your site is not yet live. Otherwise you should also write in a 301 redirect from your old URLs (mysite.com/products/complete-product-title) to your new ones at the same time.
Related
I had a question. I am creating an ecommerce website in django. There, I have categories and subcategories. When I enter to subcategory page, I able to render all products that relate to this subcategory. But, when I wanted to render all product that relate on one parent category, I am having troubles. So, I have some subcategories in one category. In that category page, I want to render all products that relate to one of subcategories of this category. Can you help me please?
models.py
class Category(models.Model):
parent = models.ForeignKey('self', related_name='children', on_delete=models.CASCADE, blank=True, null=True)
title = models.CharField(max_length=255)
slug = models.SlugField(max_length=255)
image = models.ImageField(null=True, blank=True, verbose_name="Изображение")
ordering = models.IntegerField(default=0)
is_featured = models.BooleanField(default=False)
class Meta:
verbose_name_plural = 'Categories'
ordering = ('ordering',)
def __str__(self):
if self.parent is not None:
return f"{self.parent}/{self.title}"
return self.title
#property
def imageURL(self):
try:
url = self.image.url
except:
url = ''
return url
def get_absolute_url(self):
return '/%s/' % (self.slug)
class Product(models.Model):
category = models.ForeignKey(Category, related_name='products', on_delete=models.CASCADE)
parent = models.ForeignKey('self', related_name='variants', on_delete=models.CASCADE, blank=True, null=True)
name = models.CharField(max_length=200, verbose_name="Название продукта")
price = models.IntegerField(verbose_name="Цена")
slug = models.SlugField(max_length=255)
description = models.CharField(max_length=5000,blank=True, verbose_name="Описание:")
image = models.ImageField(null=True, blank=True, verbose_name="Изображение")
novinki = models.BooleanField(default=False, verbose_name="Новинки")
popularnye = models.BooleanField(default=False, verbose_name="Популарные")
def __str__(self):
return self.name
class Meta:
verbose_name = 'Продукты'
verbose_name_plural = "Продукты"
#property
def imageURL(self):
try:
url = self.image.url
except:
url = ''
return url
views.py
def category_detail(request, slug):
data = cartData(request)
cartItems = data['cartItems']
order = data['order']
items = data['items']
categories_for_menu = Category.objects.filter(parent=None)[:3]
categories = Category.objects.filter(parent=None)
category = get_object_or_404(Category, slug=slug)
**products_all = Product.objects.filter(category.parent==category)**
print(products_all)
products = category.products.all()
context = {'items' : items, 'order' : order, 'cartItems' : cartItems, 'products':products, 'category':category, 'categories':categories,'categories_for_menu':categories_for_menu,'products_all':products_all}
return render(request, "store/categories.html", context)
Here I want to save all products that relate the category in products_all. Looking forward to your help!
class Category(models.Model):
# ...
root = models.ForeignKey('self', on_delete=models.CASCADE)
# ...
def save(self, *args, **kwargs):
self.root = self.parent.root if self.parent else self
# I didn't debug it. Maybe it has some bug.
super(Category, self).save(*args, **kwargs)
# query
Product.objects.filter(category__root=(...))
I don't agree this answer:
products = Product.objects.filter(Q(category = category)|Q(category__parent = category))
When your category is more than 2 depth, it will be wrong.
You can do it using Q function:
from django.db.models import Q
category = get_object_or_404(Category, slug=slug)
products = Product.objects.filter(Q(category = category)|Q(category__parent = category))
I am working on a Django project (like LMS) and I want to filter Lectures by Subject in the template, I do it once but now I forgot how to do it. How can I do this?!
I want to filter it by foreign-key Subject in Lecture
Here is my models.py
from django.db import models
from django.utils.text import slugify
# Create your models here.
class Subject(models.Model):
Title = models.CharField(max_length=150)
Pic = models.ImageField(upload_to='media/')
Test = models.CharField(max_length=300, null=True, blank=True)
Slug = models.SlugField(unique=True, null=True, blank=True)
def save(self, *args, **kwargs):
self.Slug = slugify(self.Title, allow_unicode=True)
super(Subject, self).save(*args, **kwargs)
def __str__(self):
return self.Title
class Lecture(models.Model):
Subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
Title = models.CharField(max_length=150)
Video = models.CharField(max_length=300)
More_Info = models.TextField()
Audio_Lecture = models.CharField(max_length=300)
Lecture_Files = models.CharField(max_length=300)
Sheet = models.CharField(max_length=300)
Slug = models.SlugField(unique=True, null=True, blank=True)
def save(self, *args, **kwargs):
self.Slug = slugify(self.Title, allow_unicode=True)
super(Lecture, self).save(*args, **kwargs)
def __str__(self):
return self.Title
and here is my views.py
from Diploma.models import Lecture, Subject
from django.shortcuts import render
# Create your views here.
def Diploma(request):
return render(request, 'Diploma/diploma.html', context={
'sub' : Subject.objects.all(),
})
def Diploma_Subject(request, slug):
subject_Lecture = Subject.objects.all()
return render(request, 'Diploma/subject.html', context={
'subj' : subject_Lecture,
'lect' : Lecture.objects.filter(I do not know what I should write here)
})
If you want to filter the Lecture model based on the slug parameter you have to do the following:
lecture = Lecture.objects.filter(Slug=slug).first()
if lecture is not None:
# lecture.Subject, lecture.Title ...
Or if you want to filter based on the slug property in Subject model you can do the following:
lectures = Lecture.objects.filter(Subject__Slug=slug)
for lecture in lectures:
# lecture.Subject, lecture.Title ...
empty:
print('There are any lectures related')
There are many things to explain here but I tried to give you a minimal repoducible example so you can custom it to achieve your goal.
Feel free to leave a comment if you have an specific doubt about understanding it.
enter image description hereRelated to Django Forms and Many2Many
I have tried to look for ways to add the + to my django form when I want to add a new post to my webpage.
What I'm looking for is a similar function as the one in admin module. See picture.
I have tried to read though the docs but not sure what to look for. Have anybody build something similar?
my model looks like this
class Wine_taste(models.Model):
title = models.CharField(max_length=128, null=True, blank=True)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
category = models.ManyToManyField('Wine_Category', verbose_name='Kind of Wine',related_name='wcategory')
subcategory = models.ForeignKey('Wine_SubCategory', verbose_name='Wine type',on_delete=models.SET_NULL, null=True, blank=True)
review = models.TextField()
producer_taste = models.ForeignKey('Wine_Producer', verbose_name='Producer or shipper', on_delete=models.SET_NULL, null=True, blank=True )
grape_taste = models.ManyToManyField('Wine_Grapes', verbose_name='Grapes')
country_taste = models.ForeignKey('Wine_Country', verbose_name='Country of origin', on_delete=models.SET_NULL, null=True, blank=True )
sweetness = models.PositiveSmallIntegerField(choices=LEVELRATE_CHOICES, verbose_name='Rate Sweetness')
acid = models.PositiveSmallIntegerField(choices=LEVELRATE_CHOICES, verbose_name='Rate acid level')
fruit = models.PositiveSmallIntegerField(choices=LEVELRATE_CHOICES, verbose_name='Rate fruitness')
taste = models.PositiveSmallIntegerField(choices=RATE_CHOICES, verbose_name='Rate taste')
overall = models.PositiveSmallIntegerField(choices=RATE_CHOICES, verbose_name='Overall rate')
thumbnail = models.ImageField()
timestamp = models.DateTimeField(auto_now_add=True)
featured = models.BooleanField()
previous_post = models.ForeignKey('self', related_name='previous', on_delete=models.SET_NULL, null=True, blank=True)
next_post = models.ForeignKey('self', related_name='next', on_delete=models.SET_NULL, null=True, blank=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('wine:wine-dyn', kwargs={"id": self.id})
def get_update_url(self):
return reverse('wine:wine-update', kwargs={"id": self.id})
def get_delete_url(self):
return reverse('wine:wine-delete', kwargs={"id": self.id})
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
img = Image.open(self.thumbnail.path)
if img.height > 400:
new_height = 400
new_width = new_height / img.height * img.width
output_size = ((new_height, new_width))
img.thumbnail(output_size)
img.save(self.thumbnail.path)
#property
def get_comments(self):
return self.comments.all().order_by('-timestamp')
#property
def comment_count(self):
return Comment.objects.filter(post=self).count()
#property
def view_count(self):
return PostView.objects.filter(post=self).count()
Blockquote
this is my view:
def wine_detailed_create(request):
title = 'Create'
form = CreateForm(request.POST or None, request.FILES or None)
author = get_author(request.user)
if request.method == 'POST':
if form.is_valid():
form.instance.author = author
form.save()
return redirect (reverse('wine:wine-dyn', kwargs={'id': form.instance.id}))
context = {
'title': title,
'form': form
}
return render(request, 'wine/wine_create.html', context)
and my forms:
class CreateForm(forms.ModelForm):
class Meta:
model = Wine_taste
fields = ('title',
'country_taste',
'category',
'subcategory',
'producer_taste',
'grape_taste',
'review',
'thumbnail',
'sweetness',
'acid',
'fruit',
'taste',
'overall',
'featured',
'previous_post',
'next_post',)
Blockquote
br
Lars
I have two models, not connected by ForeignKey. I would to display them on one page in admin interface, in the way like inlines in Django.
I can't associate PostFile with Post, because uploaded medias should be available to every Post which was and will be created in future. I have no idea what I can do if inlines are unavailable.
class Post(models.Model):
STATUS_CHOICES = (
('draft', 'Draft'),
('published', 'Published')
)
title = models.CharField(max_length=255)
slug = models.SlugField(max_length=255, unique_for_date='publish_date', blank=True)
author = models.ForeignKey(User, related_name='blog_posts', on_delete=models.CASCADE, blank=True)
content = MarkdownxField()
publish_date = models.DateTimeField(default=timezone.now)
creation_date = models.DateTimeField(auto_now_add=True)
last_update_date = models.DateField(auto_now=True)
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
tags = models.ManyToManyField(Tag)
objects = models.Manager()
class Meta:
ordering = [
'-publish_date'
]
def __str__(self):
return self.title
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
super(Post, self).save(*args, **kwargs)
def get_absolute_url(self):
return reverse('blog:post_detail',
args=[
self.publish_date.strftime('%Y'),
self.publish_date.strftime('%m'),
self.publish_date.strftime('%d'),
self.slug
])
#https://github.com/neutronX/django-markdownx/issues/83
def formatted_markdown(self):
return markdownify(self.content)
class PostFile(models.Model):
file_name = models.CharField(max_length=200, blank=True)
file_object = models.FileField(upload_to='post_files/')
file_upload_date = models.DateTimeField(auto_now_add=True)
def save(self, *args, **kwargs):
if not self.file_name:
self.file_name = self.file_object.name
super(PostFile, self).save(*args, **kwargs)
def file_url(self):
return self.file_object.url
def __str__(self):
return self.file_name
I would to receive in output a Post model admin page, where on the bottom I have listed all PostFile objects. This will give me easy and fast acces to PostFile-object url on media folder. I don't want each time to open new tab, and to go to PostFile model admin page to check url of object.
I have a page listing articles based on categories.
The category is filtered from the slug in the url.
I want to display a tag in the template with the category name.
With get_context_data I am able to retrive the slug and use it as {{categoryslug}} But how do I get the name of the category given the slug?
Model.py
class Category(models.Model):
name = models.CharField(max_length=50,unique=True, blank=True)
slug = models.SlugField(max_length=100,unique=True, blank=True)
def __str__(self):
return self.name
class Article(models.Model):
title= models.CharField(max_length=40, blank=True)
category = models.ForeignKey('Category', on_delete= models.PROTECT, related_name="pcat", blank=True)
def __str__(self):
return self.title
View.py
class ListArticleView(ListView):
model = Article
context_object_name = "listcat"
template_name = "myapp/categories.html"
paginate_by = 5
def get_queryset(self):
return Article.objects.filter(category__slug=self.kwargs['cat_slug'])
def get_context_data(self, **kwargs):
context=super(ListArticleView,self).get_context_data(**kwargs)
context['categoryslug']=self.kwargs['cat_slug']
return context
URLS.py
path(('/<cat_slug>'), views.ListArticleView.as_view(), name='some_categories'))