1)
class User(AbstractUser):
...
follower_set = models.ManyToManyField("self", blank=True)
following_set = models.ManyToManyField("self", blank=True)
self를 지정함으로써 ManyToMany 관계를 User 간의 관계로 만들 수 있다. 최초 follow가 없을 수도 있으니 blank는 허용해준다
2)
def index(request):
suggested_user_list = get_user_model().objects.all()\
.exclude(pk=request.user.pk)
return render(request, "instagram/index.html", {
"suggested_user_list": suggested_user_list,
})
자신을 제외한 유저 인스턴스를 User가 아닌 get_user_model()로 취하고 이를 리스트로 넘겨줘서 sidebar에서 순회하는데 사용한다.
3)
Input
path("client/<int:pk>/", name="client1")
+
{% url "client1" 1234 %}
Output
/client/1234/
Input
path("<int:pk>/client/", name="client2")
+
{% url "client2" 1234 %}
Output
/1234/client/
URL Reverse에서는 path/re_path에 지정된 URL 문자열을 생성해준다. {% url "client1" 1234 %} 와 {% url "client2" 1234 %} 는 URL 문자열이 어떻게 조합되는 지 신경쓰지 않아도, path name과 인자만 넘겨주면 된다. URL reverse 코드는 같아도 참조하는 코드에 따라서 값이 달라질 수 있는 것이다.
4)
@login_required
def user_follow(request, username):
follow_user = get_object_or_404(User, username=username, is_active=True)
messages.success(request, f"{follow_user} 님을 팔로우 했습니다.")
redirect_url = request.META.get("HTTP_REFERER", "root")
return redirect(redirect_url)
instagram 앱에서는 get_object_or_404 사용 시에 get_user_model() 함수가 들어갔는데 여긴 User의 본진인 accounts 앱이니까 Model을 그대로 사용할 수 있다. username은 urls.py에서 정규 표현식으로 넘어왔다.
request.META는 HttpRequest의 헤더를 담고있는 딕셔너리이다. 그 중 HTTP_REFERER는 이전 페이지에 대한 정보가 들어 있는 헤더인데 이것이 없을 경우 "root"를 가져온다.
follow 버튼의 링크를 주소창에 직접 입력했을 때 root 페이지와 연결된 /instagram/ 페이지로 이동한 것을 봐서 referer가 없음을 확인할 수 있었다.
"root"는 패턴 네임이지만, redirect가 패턴 네임으로서 먼처 처리해보고 없으면 url로서 접근한다. 한마디로 url 값과 패턴 네임을 둘 다 처리할 수 있다.
5)
@login_required
def index(request):
post_list = Post.objects.all()\
.filter(
Q(author=request.user) |
Q(author__in=request.user.following_set.all())
)
Q를 통해서 user가 팔로잉 하는 user의 포스트와 user 자신의 포스트만 post_list로 반환하도록 선택할 수 있다.
6)
if request.user.is_authenticated:
is_follow = request.user.following_set.filter(pk=page_user.pk).exists()
else:
is_follow = False
+
{% if is_follow %}
<a href="{% url "user_unfollow" page_user.username %}">Unfollow</a>
{% else %}
<a href="{% url "user_follow" page_user.username %}">Follow</a>
{% endif %}
유저 페이지에 들어갔을 때 요청 유저의 following_set에 접근 유저의 pk기 있다면 팔로우 상태임을 알 수 있다. 로그인, 인증 상태가 아니라면 겸상조차 불가능함.
views.py의 user_page 함수에 is_follow를 정의해주고 user_page.html에서 팔로우 여부에 따라 링크를 제공할 수 있다.
7)
class Post(models.Model):
...
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
+
class BaseModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class Post(BaseModel):
생성 시각과 업데이트 시각에서 add 유/무에 주의하자. add가 붙은 경우는 장고 모델이 최초 저장될 때만 현재 날짜로 저장하고. auto_now는 save 때마다 해당 작업을 수행한다.
비슷한 필드를 일일히 추가해주고 싶지 않으면 기본이 되는 모델을 만들고 abstract 옵션을 이용해서 실제 db 테이블은 만들어지지 않고 부모 클래스로서만 존재하게 할 수 있다.
Post가 BaseModel을 상속하는데 코드만 바뀌고 모델이 바뀐 건 아니므로, makemigrations-migrate 과정을 다시 진행할 필요는 없다.