[Django] 비 SPA 방식으로 장고 Forms/Views를 적극 활용한 인스타그램 St 만들기#10_User 모델에 Follow_Unfollow 관계 필드를 구현하고, Follow 기능 구현

아직·2022년 7월 15일
0
post-thumbnail

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 과정을 다시 진행할 필요는 없다.

0개의 댓글