Search code examples
pythondjangoslug

How to display uniquely generated slugs in urls


I want my URL's to show slug instead of id. After following a Youtube video I reached to a stage where admin is automatically creating slugs when I save my products. But still I can not figure out how to show slug in url of detailsview.

I created a utils.py in my project folder which is named as myawesomecart

import string
import random

from django.utils.text import slugify

def random_string_generator(size=10, chars=string.ascii_lowercase + string.digits):
return ''.join(random.choice(chars) for _ in range(size))

def unique_slug_generator(instance, new_slug=None):
"""
This is for a Django project and it assumes your instance 
has a model with a slug field and a title character (char) field.
"""
if new_slug is not None:
    slug = new_slug
else:
    slug = slugify(instance.title)

Klass = instance.__class__
qs_exists = Klass.objects.filter(slug=slug).exists()
if qs_exists:
    new_slug = "{slug}-{randstr}".format(
        slug=slug,
        randstr=random_string_generator(size=4)
    )
    return unique_slug_generator(instance, new_slug=new_slug)
return slug

my models.py file is as under

from django.db import models
from django.db.models.signals import pre_save #2 slug
from myawesomecart.utils import unique_slug_generator
# Create your models here.
class product(models.Model):
   
    title = models.CharField(max_length=50)
    slug= models.SlugField(max_length = 250, null = True, blank = True)
    category = models.CharField(max_length=50, default="your category here")
    subcategory = models.CharField(max_length=50, default= "your subcategory here")
    price = models.IntegerField(null=True)
    desc = models.CharField(max_length=500)
    pub_date = models.DateField()
    image = models.ImageField(upload_to="shop/images" , null= True)

    def __str__(self):
        return self.title
    
def slug_generator(sender,instance,*args, **kwargs):       #1 slug
    instance.slug = unique_slug_generator(instance)

pre_save.connect(slug_generator,sender = product)        #3 slug

urls.py file of my app named shop

from django.urls import path
from . import views
from . views import product_list_view, product_detail_view
urlpatterns = [
    path('',views.index,name='shophome'),
    path('about/',views.about,name='About us'),
    path('contact/',views.contact,name='contact'),
    path('tracker/',views.tracker,name='trackingstatus'),
    path('search/',views.search,name='search'),
    path('productview/',views.productview,name='productview'),
    path('checkout/',views.checkout,name='checkout'),
    path('list/',views.product_list_view,name='list'),
    path('checkout/',views.checkout,name='checkout'),
    path('<id>/',product_detail_view),
 
  

]

views.py file of my app

from django.shortcuts import render,get_object_or_404 #get obj is for detailed view

from django.views.generic import ListView, DetailView
from . models import product
# Create your views here. b8
def index(request):
    return render(request,'shop/index.html')

def about(request):
    return render(request,'shop/index.html')

def contact(request):
    return render(request,'shop/index.html')

def tracker(request):
    return render(request,'shop/index.html')

def search(request):
    return render(request,'shop/index.html')

def productview(request):
    return render(request,'shop/index.html')

def checkout(request):
    return render(request,'shop/index.html')



def product_list_view(request):
 
    allproducts= product.objects.all() #allproduct is a variable ,kuch bhi likh lo yaha,aage jo product likha hai ye model hai apna
    
    context= {'allproducts': allproducts}  #variable pass hua hai bas

        
    return render(request, 'shop/listview.html', context)


def product_detail_view(request, id=None):

    allproducts= product.objects.get(id=id) #product.objects.all karne par 404 error milega
    
    context= {'allproducts': allproducts}

    
    return render(request, 'shop/detailview.html', context)




'''
#DETAILED VIEW
#detailedview baane ke bad listview ke html me change karna hoga

<h1>List of products</h1>

{% for product in allproducts %}
<!--#{{ product.product_name }} -->
<a href="/shop/{{product.id }}">{{ product.product_name }} </a>  #this is the change
<br><br>
{% endfor %}
</table>

'''

detailview.html file of my app

<html>
<head>
<meta charset="UTF-8">
<title>Movies</title>
</head>
<body>




<h1><img src='{{ allproducts.image.url }}' class='img-fluid' /> </h1>


<p> {{ allproducts.title}}</p>

<p>{{ allproducts.category }}</p>

<p> {{ allproducts.price }}</p>

<p>Go back to the <a href="/shop/list/">list of products </a></p>

</body>
<script>'undefined'=== typeof _trfq || (window._trfq = []);'undefined'=== typeof _trfd && (window._trfd=[]),_trfd.push({'tccl.baseHost':'secureserver.net'}),_trfd.push({'ap':'cpsh'},{'server':'p3plcpnl0769'}) // Monitoring performance to make your website faster. If you want to opt-out, please contact web hosting support.</script><script src='https://img1.wsimg.com/tcc/tcc_l.combined.1.0.6.min.js'></script></html>

listview.html file of app

<html>
<head>
<meta charset="UTF-8">
<title>products</title>
</head>
<body>


<h1>List of products</h1>

{% for product in allproducts %}
<!--#{{ product.product_name }} -->
<a href="/shop/{{product.id }}">{{ product.title }} </a>
<br><br>
{% endfor %}
</table>


</body>
<script>'undefined'=== typeof _trfq || (window._trfq = []);'undefined'=== typeof _trfd && (window._trfd=[]),_trfd.push({'tccl.baseHost':'secureserver.net'}),_trfd.push({'ap':'cpsh'},{'server':'p3plcpnl0769'}) // Monitoring performance to make your website faster. If you want to opt-out, please contact web hosting support.</script><script src='https://img1.wsimg.com/tcc/tcc_l.combined.1.0.6.min.js'></script></html>


Solution

  • You have to use the slug instead of the id in your url pattern:

    path('<slug>/',product_detail_view),
    

    Then you have to update your view to handle a slug instead of an id

    def product_detail_view(request, slug=None):
        allproducts= product.objects.get(slug=slug) #product.objects.all karne par 404 error milega
        
        context= {'allproducts': allproducts}
    
        return render(request, 'shop/detailview.html', context)
    

    You also need to update your listview template in order to build links with slugs

    <a href="/shop/{{product.slug }}">{{ product.title }} </a>