How to change url to show description instead of id with django? - django

I am building my first website. But instead of the page being products/15 (where 15 is the product id), I want it to show the description of the product. How can I achieve that?
Here are my models.py:
class Product(models.Model):
name = models.CharField(max_length=100)
category = models.CharField(max_length=100)
description = models.TextField()
def __str__(self):
return self.name
views.py:
def product(request, id):
return render(request, 'product.html', {})
urls.py:
urlpatterns = [
path('products/<id>/', post, name='sub-detail'),
]
Thanks in advance for the help!

As babak gholamirad saids you should use a key that is UNIQUE in the model and the URL, just as id is but not description.
But, if you want to use and display a more "descriptive" KEY than the id in the URL: Django's slugs are made for that.
What is a "slug" in Django?
https://docs.djangoproject.com/en/3.0/ref/models/fields/#slugfield
models.py
class Product(models.Model):
name = models.CharField(max_length=100)
slug = models.CharField(max_length=256, unique=True)
category = models.CharField(max_length=100)
description = models.TextField()
def __str__(self):
return self.name
views.py:
def product(request, slug):
product_object = Product.objects.get(slug=slug)
...
return render(request, 'product.html', {})
urls.py:
urlpatterns = [
path('products/<slug>/', post, name='sub-detail'),
]
usage:
https://<my-domain>/products/my-wonderfull-descriptive-product-name/

Related

Django nested categories - path getting 404 on child slug

I try to create nested categories with django-treebeard.
And in admin this solution work fine. But I need to show this categories. When I try reach root category eq.: /kategorie/wegetarianskie then works fine, but when I try to reach fullslug with children category eq.: /kategorie/wegetarianskie/obiady then I have error 404 and don't know why.
Maybe someone have answer.
My model:
class Category(MP_Node):
name = models.CharField(max_length=255, verbose_name='Nazwa kategorii')
slug = AutoSlugField(populate_from='name', unique=True)
fullslug = models.CharField(max_length=255, verbose_name='Pełny adres kategorii', blank=True)
created_at = models.DateTimeField(default=datetime.datetime.now)
node_order_by = ['name']
Then with signal I create full slug like this:
#receiver(post_save, sender=Category)
def save_parentname(sender, instance, **kwargs):
if not instance.fullslug:
if instance.is_root():
instance.fullslug = instance.slug
instance.save()
else:
catslug = '/'.join([instance.get_parent().slug, instance.slug])
instance.fullslug = catslug
instance.save()
When child category is save, then signal create full slug like: /kategorie/wegetarianskie/obiady
I have also get_absolute_url:
def get_absolute_url(self):
return reverse('categorydetail',
args=[self.fullslug])
View:
def category_detail(request, fullslug):
category = Category.objects.get(fullslug=fullslug)
return render(request, 'categories/category_detail.html', { 'category': category })
Urls:
urlpatterns = [
path('wszystkie', views.category_list, name='categorylist'),
path('<fullslug>/', views.category_detail, name='categorydetail'),
]

How to redirect for last page visit DJANGO PYTHON

good evening! Hope everything is great!
Well, I have a little problem with redirect in Django, I was trying to make a comment section for my web app but after "posting" the comment, I can only redirect the user to my homepage (through return redirect ('/')
Models.py:
class Task(models.Model):
title = models.CharField(max_length=50)
content = models.TextField(blank=True)
date_created = models.DateTimeField(auto_now_add=True)
due_date = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, null=True, on_delete=models.SET_NULL, default=User)
responsable = models.ForeignKey(User, null=True, on_delete=models.SET_NULL, related_name="author", default=author)
STATUS_CHOICES = [('D', 'Done'),('P','Doing'),('N','Not done')]
Status = models.CharField(max_length=1,choices=STATUS_CHOICES, default='N')
IMPORTANCE_CHOICES = [('H', 'High'),('M','Medium'),('L','Low')]
importance = models.CharField(max_length=1,choices=IMPORTANCE_CHOICES, default='M')
DEPARTAMENT_CHOICES = [('D', 'Dev'),('M','Marketing'),('H','HR'),('L','Legal'),('F','Finances'),('O','Others')]
departament = models.CharField(max_length=1,choices=DEPARTAMENT_CHOICES, default='M')
is_public = models.BooleanField(default=False)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("task-detail", kwargs={"pk": self.pk})
class Comment(models.Model):
task = models.ForeignKey(Task, on_delete=models.CASCADE, related_name='comments')
author = models.ForeignKey(User, null=True, on_delete=models.SET_NULL, default=User)
body = models.TextField(help_text='Add a comment')
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=True)
class Meta:
ordering = ('-created',)
def __str__(self):
return '%s - %s' % (self.task.title, self.author)
Views.py:
class CommentCreate(CreateView):
model = Comment
fields = ['body']
def form_valid(self, form):
form.instance.task_id = self.kwargs['pk']
form.instance.author = self.request.user
form.instance.created = timezone.now()
form.save()
return redirect('/')
urls.py
from django.urls import path, include
from . import views
from .views import (DashboardTaskAppView,
TaskDetailView,
TaskCreateView,
TaskUpdateView,
DashboardTaskAppViewPublic,
TaskDeleteView,
CommentCreate)
urlpatterns = [
path('', views.index_app, name="home-app"),
path('task/', DashboardTaskAppView.as_view(), name="task-home"),
path('task/public', DashboardTaskAppViewPublic.as_view(), name="task-home-public"),
path('task/new_task/', TaskCreateView.as_view(), name='task-create'),
path('task/<int:pk>/', TaskDetailView.as_view(), name='task-detail'),
path('task/<int:pk>/update', TaskUpdateView.as_view(), name='task-update'),
path('task/<int:pk>/delete', TaskDeleteView.as_view(), name='task-delete'),
path('task/<int:pk>/comment', CommentCreate.as_view(), name='task-comment'),
path('docs/', views.doc_taskapp, name="task-doc"),
]
What I would like to do is for after submit, to redirect the user for task detail where the comment lies.. I tried return redirect('task-details'), but didn't worked. After reading the docs, I think the problem is that I'm trying to access info(data) from Task(model) in Comment(model) and I'm not doing right, is possible to do that? And is possible to call class CommentCreate(CreateView) form in a modal? or is absolutely required to have a template.html?
Thanks in advance for your time and help!
Looks like you're looking for a redirect to the task detail page with the PK, so:
for function view:
return redirect('task-detail', pk=pk)
Update: for class based view:
return redirect('task-detail', self.kwargs['pk'])
Thanks to #Ed Kohler I found out, you gave me the correct line of thought! Thanks!
class CommentCreate(CreateView):
model = Comment
fields = ['body']
def form_valid(self, form):
form.instance.task_id = self.kwargs['pk']
form.instance.author = self.request.user
form.instance.created = timezone.now()
form.save()
return redirect('task-detail', pk=self.kwargs['pk'])
Thanks again!

How to construct url path with two detailview

I have following url patterns
urlpatterns = [
path('', CategoryListView.as_view(), name='forum'),
path('category/<int:pk>/', CategoryDetailView.as_view(), name='forum-detail'),
]
Inside the CategoryDetailView I will list the posts related to that category. So, when I click the any of the post I want the post detail view inside the category, because when I will create post with CreateView class I want category already predefined. I could also do the following
path('post/<int:pk>/', ForumPostDetailView.as_view(), name='forum-post-detail'),
path('post/new/', ForumPostCreateView.as_view(), name='forum-post-create'),
]
In this case user should choose the category by himself when he or she will try to create the post. But I want the category to be chosen already something like this ( I know this is wrong)
path('category/<int:pk>/post/<int:pk>/', ForumPostDetailView.as_view(), name='forum-post-detail'),
path('category/<int:pk>/post/new/', ForumPostCreateView.as_view(), name='forum-post-create'),
]
my view file and models are like this:
class ForumPostCreateView(CreateView):
model = PostForum
fields = ['title', 'content']
template_name = 'forum/forum_post_form.html'
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class Category(models.Model):
image = models.ImageField(default='category.png', upload_to='category_pics')
title = models.CharField(max_length=100)
content = models.CharField(max_length=1200)
date_posted = models.DateTimeField(auto_now=True)
class PostForum(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
So how to do it? Now it shows
IntegrityError
null value in column "category_id" violates not-null constraint
Set the category.id in urls.py
path('category/<int:cat_id>/post/new/', ForumPostCreateView.as_view(), name='forum-post-create')
check if it exists in your view
class ForumPostCreateView(CreateView):
category = None
def despatch(self, *args, **kwargs):
super().despatch(*args, **kwargs)
if 'cat_id' in self.kwargs:
self.category = Category.objects.get(id=cat_id)
Now you can use it in your form when you initialise the form

How to get slugs from different models in the urls i.e path(<slug:slug1>/<slug:slug2>/)?

Im trying to get a ur pattern that looks like this WWW.domain.com/slug1/slug2, where slug1 is the foreignkey to slug to. Think of it like library.com//. The author and book are two different models, both with their own slug. Is there a way where i can import the slug from author to the detailview of the book and then use it in the urls for the book detailview?
This is how i imagine the path to look like:
path('brands/<slug:brand_slug>/<slug:model_slug>', views.Brand_ModelsDetailView.as_view(), name='model-detail'),
These are my models:
class Brand(models.Model):
brand_name = models.CharField(
max_length=50, help_text='Enter the brand name',)
slug = AutoSlugField(populate_from='brand_name', default = "slug_error", unique = True, always_update = True,)
def get_absolute_url(self):
"""Returns the url to access a particular brand instance."""
return reverse('brand-detail', kwargs={'slug':self.slug})
def __str__(self):
return self.brand_name
class Brand_Models(models.Model):
name = models.CharField(max_length=100)
brand = models.ForeignKey('Brand', on_delete=models.SET_NULL, null=True)
slug = AutoSlugField(populate_from='name', default = "slug_error_model", unique = True, always_update = True,)
def get_absolute_url(self):
"""Returns the url to access a particular founder instance."""
return reverse('model-detail', kwargs={'slug':self.slug})
def __str__(self):
return self.name
My current attempt at the views:
class Brand_ModelsDetailView(generic.DetailView):
model = Brand_Models
def get_queryset(self):
qs = super(Brand_ModelsDetailView, self).get_queryset()
return qs.filter(
brand__slug=self.kwargs['brand_slug'],
slug=self.kwargs['model_slug']
)
EDIT:
class RefrenceDetailView(generic.DetailView):
model = Refrence
def get_queryset(self):
qs = super(RefrenceDetailView, self).get_queryset()
return qs.filter(
brand__slug=self.kwargs['brand_slug'],
model__slug=self.kwargs['model_slug'],
slug = self.kwargs['ref_slug']
)
Your error is just with the get_absolute_url method of Brand_Models; because the detail URL takes two slugs, you need pass them both to reverse there.
return reverse('model-detail', kwargs={'brand_slug': self.brand.slug, 'model_slug':self.slug})

Save comment.id to another object

I want to save in my database the comment id which has been commented. For that I have two models: Comentario and Pregunta. Look below:
models.py
class Comentario (models.Model):
titulo = models.CharField(max_length=50)
texto = models.CharField(max_length=200)
autor = models.ForeignKey (Perfil, null=True, blank=True, on_delete=models.CASCADE)
fecha_publicacion = models.DateTimeField(auto_now_add=True)
tag = models.ManyToManyField(Tags, blank=True)
def __str__(self):
return (self.titulo)
class Pregunta (models.Model):
descripcion = models.CharField(max_length=150)
autor = models.ForeignKey (Perfil, null=True, blank=True, on_delete=models.CASCADE)
fecha_pregunta = models.DateTimeField(auto_now_add=True)
comentario_preguntado = models.ForeignKey(Comentario, null=True, blank=True, related_name="pregunta_set")
def __str__(self):
return (self.descripcion)
When a comment is commented I want to save the 'comentario' id as 'comentario_preguntado' id. For that I have created the next view:
views.py
def ComentarioListar2 (request):
aa=Puesto.objects.filter(nombre_puesto=request.user.nom_puesto).values_list('etiquetas')
bb=Tags.objects.filter(id__in=aa)
objects=Comentario.objects.filter(tag__in=bb).exclude(autor__id=request.user.id)
form = preguntaform(request.POST or None)
if request.method == 'POST' and form.is_valid():
form.instance.autor = request.user
form.instance.comentario_preguntado=request.comentario.id
form.save()
return render(request, 'home/comentario_listar.html', {'objects': objects, 'form': form})
urls.py
urlpatterns = [
url(r'^listar2$', views.ComentarioListar2, name="listar2"),
]
But I obtain this error "ComentarioListar2() missing 1 required positional argument: 'Comentario_id'"
I do not know how to save in the comentario_preguntado id the id of the comment it is commented (comentario_id).
thank you for your help
Your URL needs to be declared so that the primary key of the model instance can be referred to from the view.
It should be like this:
url(r'^listar2/(?P<Comentario_id>[0-9]+)/$', views.ComentarioListar2, name="listar2"),
So, an example of the URL would be /listar2/101/. Where 101 is the ID of your Comentario model instance.
Then, you can access it in the view with the function you have defined:
def ComentarioListar2 (request, Comentario_id):
^^^^^^^