Aula 20 – Loja Online – Django – Reverse URL
Aula 20 – Loja Online – Django – Reverse URL
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. 😉