Aula 20 – Loja Online – Django – Reverse URL

More videos
Views
   

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

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.

Voltar para página principal do blog

Todas as aulas desse curso

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. 😉

 

Increva-se

Inscreva-se agora e receba um e-mail assim que eu publicar novo conteúdo.

Concordo em me inscrever no blog Código Fluente

Você poderá cancelar sua inscrição a qualquer momento.

(Visited 54 times, 1 visits today)
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>