Coding / Programming Videos

Post your favorite coding videos and share them with others!

ORM, Querysets e onde habitam. (Sistema de Likes)

Source link

Python/Django logo

Hey! Depois de um tempo trampando na Europa reavivei meu espírito Blogger. Hoje vou comentar um pouco sobre os Querysets de Django ou qualquer outra ORM que a gente costuma utilizar no dia-a-dia de desenvolvedor. Minha principal motivação foi a de que ultimamente eu ando fazendo alguns testes para umas vagas de desenvolvedor aqui no Brasil e esses dias me deparei com um teste em qual 60% dele era composto de perguntas de SQL, não acho ruim, mas o meu feedback pra empresa foi mais ou menos “Hey, caras, ninguém mais escreve SQL na mão assim”. Esse foi um dos grandes choques de realidade que vivi na Europa, os desenvolvedores em geral tentar abstrair TUDO pra ficar o mais longe possivel das queries manuais SQL. E graças ao nosso querido universo isso acontece e a tendência é só aumentar.

Eu estou considerando que você tenha conhecimento intermediário em Django. Aqui vou implementar um sistema de Likes em fotos. O contexto é um projeto que fiz baseado em uma galeria virtual.

O primeiro passo depois de ter o projeto e app criado (vou deixar essa parte com vocês, o intuíto aqui não é mostrar isso) é ter um objeto Like, e depois um objeto relacionado as fotos adicionadas ao sistema, existem opções já prontas pela net, só fazer uma classe que herda os atributos dessas opções prontas. No meu caso eu utilizei a lib Photologue.

class Like(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
picture = models.ForeignKey(SitePhoto, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
class SitePhoto(Photo):
approved = models.BooleanField(default=False)
user = models.ForeignKey(User, on_delete=models.CASCADE)

@property
def likes(self):
likes_for_this_pic = Like.objects.filter(picture_id=self.id)
return likes_for_this_pic.count()

def get_absolute_url(self):
return reverse('photos:photo_detail', args=[self.slug])

A classe Like possue uma referência ao usuário, foto e quando foi criada. Na hora de criar esses Models você teria primeiro que fazer as migrations para o Model SitePhoto, e depois incluir a propriedade likes para ele. Se você fizer tudo junto vão subir alguns erros. A classe SitePhoto herda todos os atributos da Classe pai que é Photo. No caso eu implementei apenas uma propriedade que retorna a contagem de likes dessa foto. Reparem o filtro, ele faz uma busca simples no banco de dados para todas os Likes que fazem referencia a essa instancia especifica de SitePhoto, e retorna um Queryset com todos esses objetos, eu retorno um Inteiro que representa a soma de todos os Likes na foto com o método Count().

Bom, vamos criar uma URL para essa ação que é deixar o Like em alguma coisa, estamos trabalhando com Django puro e seus custom templates, recomendo que usem alguma framwork JS e uma API Rest, mas para motivos de aprendizado é válido.

urlpatterns = [ path(r'photo/<photo_id>/like/',PhotoLikeView.as_view(), name='photo_like'),]

Nas suas views você cria uma view especifica para a ação do Like. E é importante implementar uma lógica para não deixar que um usuário de Like mais que uma vez em cada Foto, em Django eu utilizo Class Based Views (OOP maniac) por motivos de: É MUITO MAIS FÁCIL.

class PhotoLikeView(LoginRequiredMixin, CreateView):
"""
Basically in this view, I made a custom validation
to check whether the user already liked that specific
picture, first I get all the likes he has in the DB
if appears a like related to this Photo instance, it gets
rejected and the counter does not continue. Just one like
per user in each photo.
"""
success_url = reverse_lazy('photos:index')
model = Like
fields = ['user', 'picture', ]
queryset = Like.objects.all()

def form_invalid(self, form):
return redirect(reverse('photos:index'))

def form_valid(self, form):
picture_id = int(self.kwargs['photo_id'])
pictures_that_user_liked = Like.objects.filter(user=form.instance.user)
for like in pictures_that_user_liked:
if like.picture.id == picture_id:
return self.form_invalid(form)
return super(PhotoLikeView, self).form_valid(form)

def get_form_kwargs(self):
kwargs = super(PhotoLikeView, self).get_form_kwargs()
kwargs['data'] = {
'user': self.request.user.id,
'picture': self.kwargs['photo_id']
}
return kwargs

O Like é um botão dentro de um form no HTML e irá se comunicar através de algum verbo HTTP. No caso da minha CreateView, eu sobreescrevi a função form_valid. Ou seja, quando algum usuário enviar um POST Request pro meu Backend, ele vai validar esse Like de maneira personalizada. No caso eu pego o meu parâmetro de URL que corresponde ao ID da minha foto e coloco em memória, depois disso eu pego todas as fotos que o usuário deu Like no banco de dados e faço um check, se aquela foto já estiver no Queryset de fotos que o usuário curtiu eu simplemente retorno um form_invalid. Na função get_form_kwargs eu basicamente preencho o resto dos campos pra criar uma instância válida de Like. Olhe nossa classe de Like de novo, você vai entender.

Assim criamos uma lógica que não deixa o usuário dar 2 likes em uma mesma foto. YAY!

Agora se você quiser colocar isso num HTML pra realmente fazer as ações acontecerem ficaria mais ou menos assim:

<div class="container">
<div class="thumbnail">
<div class="title">
<p>{{ photo.title }}</p>
{% with photo.likes as like %}
{% if like %}
<p>Likes: {{ like }}</p>
{% endif %}
{% endwith %}
</div>
<form method="post">
{% csrf_token %}
<button
class="btn-lg btn-primary"
formaction="{% url 'photo_like' photo.id %}"
>
Like
</button>
</form>
</div>
</div>

Lembrando que o context que eu usei pra essa view é diferente da view que nós criamos para a ação do Like, ou seja, você teria que implementar um context pra sua view e passar um queryset de Fotos pro HTML, você provavelmente vai usar um loop nos templates.

Agora sim! Acho que você está pronto pra deixar seus usuários darem bastante likes por ai! 🙂

Finalmente: Meu intuíto com esse post é mostrar que conseguimos fazer MUITA coisa com as ORMs e ainda mais, conseguimos implementar lógica de programação com os Querysets retornados. Claro que pra muita coisa ainda precisaremos utilizar SQL puro, mas meu foco aqui é fazer a galera entender de que EXISTE JEITO MAIS FÁCIL e bem mais produtivo de trabalhar com banco de dados. Espero que eu tenha ensinado alguém alguma coisa nova, ou ajudado alguém que estava procurando algo do tipo em Português. 🙂

Source link

Bookmark(0)
 

Leave a Reply

Please Login to comment
  Subscribe  
Notify of
Translate »