Aula 20 – Loja Online – Django – Reverse URL

Aula 20 – Loja Online – Django – Reverse URL

Reverse url no django

Reverse url no django

Voltar para página principal do blog

Todas as aulas desse curso

Aula 19                  Aula 21

Se gostarem do conteúdo dêem um joinha 👍 na página do Código Fluente no
Facebook

Esse é o link do código fluente no Pinterest

Meus links de afiliados:

Hostinger

Digital Ocean

One.com

Para baixar o código como está até agora, acesse o link abaixo:
https://github.com/toticavalcanti/django_ecommerce/tree/reverse_url

Atualizando nossa navegação com reverse url

É hora de atualizar nossa navegação e torná-la funcional.

Como já falamos na aula 15, a principal vantagem de se usar o reverse é não codificar rotas(path) nos códigos, ou seja, evita que você trate as URLs em hard code, deixando-as mais flexíveis.

Assim, mesmo que o caminho da URL seja alterado, ele não terá efeito nos models e código-fonte das views.

Em outras palavras, ele evita que se a gente quiser mudar alguma URL do nosso ecommerce, tenhamos que fazer essa mudança em vários lugares, isto é, em vários arquivos do projeto.

No começo, quando se desenvolvia aplicações com o django, o endereço da URL era codificado em urls.py, views.py e template html files.

Isso cria um problema, se você alterar o caminho do endereço da URL de uma página em urls.py, todo lugar que usar esse caminho de URL da página (views.py e todos os template html files) precisam ser alterados também.

Se é um grande projeto, vai dá muito trabalho.

Com o reverse, se você alterar uma URL, você poderá fazer uma referência a ela assim:

reverse(nome_da_url)

O reverse funciona em templates, e também nas views.

Então vamos lá, mãos a obra!

Vamos fazer o seguinte, abra o django_ecommerce/products/urls.py e adicione o argumento name:


path('<slug:slug>/', ProductDetailSlugView.as_view(), name='detail')

django_ecommerce/products/urls.py vai ficar assim:


from django.urls import path

from .views import (
                        ProductListView, 
                        ProductDetailSlugView,
                    )

urlpatterns = [
    path('', ProductListView.as_view()),
    path('<slug:slug>/', ProductDetailSlugView.as_view(), name='detail')
]

Usar a propriedade name é um atalho para chegar a view, esse atalho pode ser descrito nos templates, bem como no método instance.get_absolute_url, então vamos fazer no template primeiro.

Abra o django_ecommerce/products/templates/products/snippets/card.html e acrescente o que tá em laranja:

<div class="card" style="width: 18rem;">
    {% if instance.image %}
        <a href="{{ instance.get_absolute_url }}">
            <img src="{{ instance.image.url }}" class="card-img-top" alt="{{ instance.title }} logo">
        </a>
    {% endif %}
    <div class="card-body">
        <h5 class="card-title">{{ instance.title }}</h5>
        <p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's
            content.</p>
        <a href="{{ instance.get_absolute_url }}" class="btn btn-primary">Detalhe</a>
        <a href="{% url 'detail' slug=instance.slug %}" class="btn btn-warning">Atalho para a URL</a>
    </div>
</div>

Veja que precisamos usar o slug=instance.slug em: href=”{% url ‘detail’ slug=instance.slug %}” 

Porque no django_ecommerce/products/urls.py temos o keyword arguments <slug>, então, para que funcione precisamos do slug=instance.slug nos templates.

Agora acesse: 127.0.0.1:8000/products/

Veja que temos um outro botão: Atalho para a URL.

Vamos fazer um teste, abra o django_ecommerce/e_commerce/urls.py e troque a linha que tem:

path(products/, include(products.urls)),

Para:

path(product/, include(products.urls)),

E acesse: 127.0.0.1:8000/product/

Se você clicar em Detalhe não vai funcionar, ele tá pegando a URL com o get_absolute_url, que ainda tá em hard-code, mas, se você clicar em Atalho para a URL, vai funcionar, porque ele tá pegando a URL via propriedade name.

Usamos a propriedade name no nosso template django_ecommerce/products/templates/products/snippets/card.html como exemplo, podemos fazer isso sem problemas, mas, não é tão robusto como usar o reverse no nosso model, porque vamos acabar nos repetindo e tendo que mexer em vários lugares, em vários arquivos, para fazer uma simples mudança nas nossas URLs.

Por isso, vamos fazer melhor, faremos o nosso django_ecommerce/products/models.py usar o reverse no método get_absolute_url.


from django.db import models
from .utils import unique_slug_generator
from django.db.models.signals import pre_save
from django.urls import reverse
#Custom queryset
class ProductQuerySet(models.query.QuerySet):
    
    def active(self):
        return self.filter(active = True)

    def featured(self):
        return self.filter(featured = True, active = True)

class ProductManager(models.Manager):
    
    def get_queryset(self):
        return ProductQuerySet(self.model, using = self._db)
    
    def all(self):
        return self.get_queryset().active()

    def featured(self):
        #self.get_queryset().filter(featured = True)
        return self.get_queryset().featured()

    def get_by_id(self, id):
        qs = self.get_queryset().filter(id = id)
        if qs.count() == 1:
            return qs.first()
        return None

# Create your models here.
class Product(models.Model): #product_category
    title       = models.CharField(max_length=120)
    slug        = models.SlugField(blank = True, unique = True)
    description = models.TextField()
    price       = models.DecimalField(decimal_places=2, max_digits=20, default=100.00)
    image       = models.FileField(upload_to = 'products/', null = True, blank = True)
    featured    = models.BooleanField(default = False)
    active      = models.BooleanField(default = True)


    objects = ProductManager()

    def get_absolute_url(self):
        #return "/products/{slug}/".format(slug = self.slug)
        return reverse("detail", kwargs={"slug": self.slug})
    
    #python 3
    def __str__(self):
        return self.title
        
    #python 2
    def __unicode__(self):
        return self.title

def product_pre_save_receiver(sender, instance, *args, **kwargs):
    if not instance.slug:
        instance.slug = unique_slug_generator(instance)

pre_save.connect(product_pre_save_receiver, sender = Product)

Obs. Veja que o antigo return do get_absolute_url, o: return “/products/{slug}/”.format(slug = self.slug) começa com /products/ em hard-code, por isso, na hora que mudamos para /product/ no singular, o código quebrou, com o reverse isso não acontece mais.

Faça o mesmo teste novamente acessando 127.0.0.1:8000/product/

Veja que agora os dois botões estão funcionando.

Agora, em django_ecommerce/e_commerce/urls.py, troque a linha que a gente tinha mudado para o teste:

path(product/, include(products.urls)),

Deixe como tava antes, assim:

path(products/, include(products.urls)),

Acesse: 127.0.0.1:8000/products/

Tudo certo? Tá funcionando?

Então, seguindo.

Temos esse name=’detail’ na url dos produtos, mas, há uma boa chance de que outros componentes também tenha a view de detalhes.

Então, o que faremos se tivermos outro componente que tenha um detail view também?

Usaremos namespace para sanar qualquer tipo de ambiguidade.

Em django_ecommerce/e_commerce/urls.py acrescente o que tá em laranja.


from django.conf import settings
from django.conf.urls.static import static

from django.contrib import admin
from django.urls import path, include

from .views import (home_page, 
                    about_page, 
                    contact_page, 
                    login_page, 
                    register_page
)

urlpatterns = [
    path('', home_page),
    path('about/', about_page),
    path('contact/', contact_page),
    path('login/', login_page),
    path('register/', register_page),
    path('products/', include("products.urls", namespace="products")),
    path('admin/', admin.site.urls),
]

if settings.DEBUG:
    urlpatterns = urlpatterns + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
    urlpatterns = urlpatterns + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Precisamos adicionar o nome da aplicação no django_ecommerce/products/urls.py para que o namespace funcione, o nome da aplicação(app_name) deve ser igual ao namespace.

Se parar pra pensar, isso melhora inclusive a semântica do código, porque quando colocamos o prefixo do namespace:

product:detail

Fica claro que se trata do detalhe de um produto.

Vamos adicionar em django_ecommerce/products/urls.py o nome da aplicação:


from django.urls import path

app_name = "products"

from .views import (
                        ProductListView, 
                        ProductDetailSlugView,
                    )

urlpatterns = [
    path('', ProductListView.as_view()),
    path('/', ProductDetailSlugView.as_view(), name='detail')
]   

Se tentar acessar 127.0.0.1:8000/products/ vai dá um products detail not found, por isso, temos que acrescentar o namespace, ou seja, o que tá em laranja em:

django_ecommerce/products/models.py


from django.db import models
from .utils import unique_slug_generator
from django.db.models.signals import pre_save
from django.urls import reverse
#Custom queryset
class ProductQuerySet(models.query.QuerySet):
    
    def active(self):
        return self.filter(active = True)

    def featured(self):
        return self.filter(featured = True, active = True)

class ProductManager(models.Manager):
    
    def get_queryset(self):
        return ProductQuerySet(self.model, using = self._db)
    
    def all(self):
        return self.get_queryset().active()

    def featured(self):
        #self.get_queryset().filter(featured = True)
        return self.get_queryset().featured()

    def get_by_id(self, id):
        qs = self.get_queryset().filter(id = id)
        if qs.count() == 1:
            return qs.first()
        return None

# Create your models here.
class Product(models.Model): #product_category
    title       = models.CharField(max_length=120)
    slug        = models.SlugField(blank = True, unique = True)
    description = models.TextField()
    price       = models.DecimalField(decimal_places=2, max_digits=20, default=100.00)
    image       = models.FileField(upload_to = 'products/', null = True, blank = True)
    featured    = models.BooleanField(default = False)
    active      = models.BooleanField(default = True)


    objects = ProductManager()

    def get_absolute_url(self):
        //return "/products/{slug}/".format(slug = self.slug)
        return reverse("products:detail", kwargs={"slug": self.slug})
    
    #python 3
    def __str__(self):
        return self.title
        
    #python 2
    def __unicode__(self):
        return self.title

def product_pre_save_receiver(sender, instance, *args, **kwargs):
    if not instance.slug:
        instance.slug = unique_slug_generator(instance)

pre_save.connect(product_pre_save_receiver, sender = Product)

Veja que ainda não funcionou!!!???

Vá no django_ecommerce/products/templates/products/snippets/card.html e acrescente o namespace também. É o que tá em laranja no código abaixo:

<div class="card" style="width: 18rem;">
    {% if instance.image %}
        <a href="{{ instance.get_absolute_url }}">
            <img src="{{ instance.image.url }}" class="card-img-top" alt="{{ instance.title }} logo">
        </a>
    {% endif %}
    <div class="card-body">
        <h5 class="card-title">{{ instance.title }}</h5>
        <p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's
            content.</p>
        <a href="{{ instance.get_absolute_url }}" class="btn btn-primary">Detalhe</a>
        <a href="{% url 'products:detail' slug=instance.slug %}" class="btn btn-warning">Atalho para a URL</a>
    </div>
</div>

Agora acesse: 127.0.0.1:8000/products/

É isso, nos vemos na próxima aula.

Aula 19                  Aula 21

Todas as aulas desse curso

Voltar para página principal do blog

Para baixar o código como está até agora, acesse o link abaixo:
https://github.com/toticavalcanti/django_ecommerce/tree/reverse_url

Se gostarem do conteúdo dêem um joinha 👍 na página do Código Fluente no
Facebook

Link do código fluente no Pinterest

Novamente deixo meus link de afiliados:

Hostinger

Digital Ocean

One.com

Obrigado, até a próxima e bons estudos. 😉

 

About The Author
-

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>