models.py
avatar_url() 메서드는 회원가입 유저가 아바타 이미지를 업로드 하였을 경우 업로드한 이미지의 주소를 리턴하고, 업로드하지 않았으면 pydenticon_image 의 이미지 주소를 리턴한다.
다른 곳에서 유효성 검사를 하지 말고, 자주 쓰이니 @property
데코레이터를 이용하여 모델단에 메서드를 구현한다.
from django.conf import settings
from django.contrib.auth.models import AbstractUser
from django.core.mail import send_mail
from django.core.validators import RegexValidator
from django.db import models
from django.template.loader import render_to_string
from django.shortcuts import resolve_url
# Create your models here.
class User(AbstractUser):
class GenderChoices(models.TextChoices):
MALE = "M", "Male"
FEMALE = "F", "Female"
website_url = models.URLField(blank=True)
bio = models.TextField(blank=True)
phone_number = models.CharField(max_length=13, blank=True, validators=[RegexValidator(r"^010-?[1-9]\d{3}-?\d{4}$")])
gender = models.CharField(max_length=1, blank=True, choices=GenderChoices.choices)
avatar = models.ImageField(blank=True, upload_to='accounts/profile/%Y/%m/%d', help_text="48px * 48px 크기의 파일을 업로드해주세요.")
@property
def name(self):
return f"{self.first_name} {self.last_name}"
@property
def avatar_url(self):
if self.avatar:
return self.avatar.url
else:
return resolve_url("pydenticon_image", self.username)
def send_welcome_email(self):
# 중략
urls.py
# instagram/urls.py
from django.urls import path, re_path
from . import views
app_name = 'instagram'
urlpatterns = [
... 중략
re_path(r'^(?P<username>[\w.@+-]+)/$',
views.user_page, name='user_page'),
]
views.py
len(post_list)
는 post_list 전체를 다 가져와서 메모리에 얹은 다음에 메모리상의 리스트의 갯수를 반환하므로 상대적으로 느리다.
# instagram/views.py
def user_page(request, username):
# urls.py에서 넘겨받은 username이
# logined된 User가 가진 필드에 존재하는가를 체크해야 한다.
page_user = get_object_or_404(get_user_model(), username=username, is_active=True) # is_active로 접근이 허용된 사람이 아니면 404
post_list = Post.objects.filter(author=page_user)
post_list_count = len(post_list)
return render(request, "instagramsns/user_page.html",{
"page_user": page_user,
"post_list": post_list,
"post_list_count": post_list_count,
})
post_list_count()
는 실데 DB에 count 쿼리를 던지므로 상대적으로 빠르다.
# instagram/views.py
def user_page(request, username):
# urls.py에서 넘겨받은 username이
# logined된 User가 가진 필드에 존재하는가를 체크해야 한다.
page_user = get_object_or_404(get_user_model(), username=username, is_active=True) # is_active로 접근이 허용된 사람이 아니면 404
post_list = Post.objects.filter(author=page_user)
post_list_count = post_list.count()
return render(request, "instagramsns/user_page.html",{
"page_user": page_user,
"post_list": post_list,
"post_list_count": post_list_count,
})
user_page.html
easy-thumnails 를 이용하여 원본 이미지를 CROP 할 경우, 원본 이미지의 크기가 512x512 보다 작을 경우 고정된 크기의 썸네일을 제공하지 못하는 문제가 있다.
# instagram/templates/user_page.html
{% extends "instagramsns/layout.html" %}
{% load thumbnail %}
{% block content %}
<div class="container">
<div class="row pt-5 pb-5">
<div class="col-sm-3" style="text-align: center;">
<img src="{{ user.avatar_url }}" class="rounded-circle" style="width: 160px; height: 160px;" />
</div>
<div class="col-sm-9">
{{ page_user.username }} <!--user.username 은 현재 로그인한 유저를 나타낸다.-->
<a href="{% url 'profile_edit' %}" class="btn btn-outline-dark btn-sm">
Edit Profile
</a>
<hr>
{{ post_list_count }} posts, 0 fllowers, 0 following
<hr>
{{ page_user.username }}
</div>
</div>
<div class="row mt-3">
{% for post in post_list %}
<div class="col-sm-4 mb-3">
<img src="{% thumbnail post.photo 512x512 crop %}" alt="{{ post.caption }}" style="width: 100%;" />
</div>
{% endfor %}
</div>
</div>