M:N

hey hey·2021년 12월 7일
0

Django

목록 보기
5/9
post-thumbnail

기존의 1:N 구조

* hospital/models.py

class Doctor(models.Model):
		name =models.TextField()
		
		def __str__(self):
				return f'{self.pk}번 의사 {self.name}'

class Patient(models.Model):
		name = models.TextField()
		doctor = models.ForeignKey(Doctor, on_delete=models.CASCADE)
	
		def __str__(self):
				return f'{self.pk}번 환자 {self.name}
* python manage.py shell_plus

In [1]: doctor1 = Doctor.objects.create(name='jstin')
In [2]: doctor2 = Doctor.objects.create(name='eric')

In [3]: patient1 = Patient.objects.create(name='tony',doctor=doctor1)
In [4]: patient2 = Patient.objects.create(name='harry',doctor=doctor2)

In [5]: doctor1  // Out[5]: <Doctor: 1번 의사 jstin>
In [7]: patient1 // Out[7]: <Patient: 1번 환자 tony>

1명의 환자가 두명의 의사에게 진료받고 싶다?

→ 새로 만들었어야 했다.

In [9]: patient3 = Patient.objects.create(name='tony',doctor=doctor2)

⭐ 1:N:1 구조

중개모델 작성법

* hospital/models.py
class Doctor(models.Model):
    name = models.TextField()

    def __str__(self):
        return f'{self.pk}번 의사 {self.name}'

class Patient(models.Model):
    name = models.TextField()

    def __str__(self):
        return f'{self.pk}번 환자 {self.name}'

# 중개모델 작성
class Reservation(models.Model):
    doctor = models.ForeignKey(Doctor, on_delete=models.CASCADE)
    patient = models.ForeignKey(Patient, on_delete=models.CASCADE)

    def __str__(self):
        return f'{self.doctor_id}번 의사의 {self.patient_id}번 환자'
* python manage.py shell_plus

In [1]: doctor1 = Doctor.objects.create(name='justin')
In [2]: patient1 = Patient.objects.create(name='tony')

In [3]: **Reservation.objects.create(**doctor=doctor1,patient=patient1**)**
Out[3]: <Reservation: 1번 의사의 1번 환자>
* **의사가 환자 기록 확인하기** 
In [4]**: doctor1.reservation_set.all()**
Out[4]: <QuerySet [<Reservation: 1번 의사의 1번 환자>]>

* **환자가 의사 기록 확인하기** 
In [5]: **patient1.reservation_set.all()**
Out[5]: ****<QuerySet [<Reservation: 1번 의사의 1번 환자>]>

****

ManyToManyField

* hospital/models.py
from django.db import models

class Doctor(models.Model):
    name = models.TextField()

    def __str__(self):
        return f'{self.pk}번 의사 {self.name}'

class Patient(models.Model):
    name = models.TextField()
    # ManyToManyField 작성
    **doctors = models.ManyToManyField(Doctor)**> 복수인 이유는 단수인 foreign키와 차별을 주기위해서
    def __str__(self):
        return f'{self.pk}번 환자 {self.name}'
* python manage.py shell_plus

In [1]: doctor1 = Doctor.objects.create(name='justin')
In [2]: patient1 = Patient.objects.create(name='tony')
In [3]: patient2 = Patient.objects.create(name='harry')

- 환자가 의사 예약하기
In [4]: **patient1.doctors.add(doctor1)   

-** 의사가 환자 목록보기
In [6]: doctor1.**patient_set**.all() // Out[6]: <QuerySet [<Patient: 1번 환자 tony>]>

**-** 의사가 환자 예약하기
In [7]: doctor1.**patient_set**.add(patient2)

- 의사가 환자 예약 취소하기
In [8]: doctor1.patient_set.**remove**(patient2)

- 환자가 의사 예약 취소하기
In [9]: patient1.doctors.remove(doctor1)
patient_set 쓰기 귀찮다 ->
doctors = models.ManyToManyField(Doctor,**related_name='patient'**)

In [3]: doctor1.patient.all() // Out[3]: <QuerySet []>

다대다 에서 추가 정보를 입력해야 할 때는

중개테이블과 through를 이용해야 한다.

class Doctor(models.Model):
    name = models.TextField()

    def __str__(self):
        return f'{self.pk}번 의사 {self.name}'

class Patient(models.Model):
    doctors = models.ManyToManyField(Doctor, **through='Reservation'**)
    name = models.TextField()

    def __str__(self):
        return f'{self.pk}번 환자 {self.name}'

class Reservation(models.Model):
    **doctor = models.ForeignKey(Doctor, on_delete=models.CASCADE)   다대다 에서의
    patient = models.ForeignKey(Patient, on_delete=models.CASCADE)  중개 테이블**
    
		**symptom =** models.TextField()
    **reserved_at =** models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f'{self.doctor.pk}번 의사의 {self.patient.pk}번 환자'
* python manage.py shell_plus
In [1]: doctor1 = Doctor.objects.create(name='justin')
In [2]: patient1 = Patient.objects.create(name='tony')
In [4]: patient2 = Patient.objects.create(name='harry')

In [5]: reservation1 = **Reservation**(doctor=doctor1,patient=patient1,**symptom='headache'**)
In [8]: reservation1**.save()**  저장을 해줘야한다.

In [12]: patient2.doctors.add(doctor1,through_defaults={'symptom':'flu'})

In [14]: Reservation.objects.all()
Out[14]: <QuerySet [<Reservation: 1번 의사의 1번 환자>, <Reservation: 1번 의사의 2번 환자>]>

In [19]: patient2.doctors.remove(doctor1)

👍 LIKE (M:N)

MtoM 사용시 1:N에서의 manager 이름 → ex) article.user_set 이 중복되기 때문에

related_name='' 으로 바꿔주는게 필수

class Article(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    like_users = models.ManyToManyField(settings.AUTH_USER_MODEL, **related_name='like_articles'**)
    title = models.CharField(max_length=10)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.title

class Comment(models.Model):
    article = models.ForeignKey(Article, on_delete=models.CASCADE)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    content = models.CharField(max_length=200)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.content

urls.py

path('<int:article_pk>/likes/',views.likes,name='likes'),

views.py

def likes(request,article_pk):
    article = get_object_or_404(Article,pk=article_pk)
    현재 좋아요를 요청하는 회원(request.user)이 
    해당 게시물의 좋아요를 누른 회원 목록에 있다면,
		**if article.like_users.filter(pk=request.user.pk).exists():**
    # == if request.user in article.like_users.all() : 좋아요 취소
        article.like_users.remove(request.user)
    else:             # 좋아요 하기
        article.like_users.add(request.user)
    return redirect()

if request.user in article.like_users.all() 이렇게 써도 되지만

if article.like_users.filter(pk=request.user.pk).exists(): 가 더 권장된다.

왜 ? → 규모가 큰 QuerySet의 특정 개체 존재 여부확인에 더 적합

@require_POST            **추가코드 작성**
def likes(request,article_pk): 
   # if request.user.is_authenticated():
        article = get_object_or_404(Article,pk=article_pk)
        # 현재 좋아요를 요청하는 회원(request.user)이 
        # 해당 게시물의 좋아요를 누른 회원 목록에 있다면,
        if article.like_users.filter(pk=request.user.pk).exists():
        # if request.user in article.like_users.all() :  # 좋아요 취소
            article.like_users.remove(request.user)
        else:             # 좋아요 하기
            article.like_users.add(request.user)
        return redirect('articles:index')
    # return redirect('accounts:login') 

index.html

		<div>
      <form action="{% url 'articles:likes' article.pk %}" method='POST'>
        {% csrf_token %}
        {% if user in article.like_users.all %}
          <input type="submit" value='좋아요 취소'>
        {% else %}
          <input type="submit" value='좋아요'>
        {% endif %}
      </form>
    </div>

🧑🏻‍🔧 PROFILE

* accounts/urls.py
path('<username>/',views.profile,name='profile'),
* accounts/views.py

def profile(request,username):
    person = get_object_or_404(get_user_model(),username=username)
    context ={
        'person':person
    }
    return render(request,'accounts/profile.html',context)
* base.html
<p>작성자 : <a href="{% url 'accounts:profile' article.user.username %}">{{ article.user }}</a></p>

🦜 Follow

* accounts/models.py
class User(AbstractUser):
    followings = models.ManyToManyField('self',symmetrical=False,related_name='followers')

symmetrical

symmetrical =True(기본값) 으로 되어 있을 때는 a>b 일경우 b>a 까지 같이된다.

그래서 False로 바꿔줘야 한다.

* accounts/views
def follow(request,user_pk):     me you 로 만들면 재밌음
		me = requets.user
    person (you)= get_object_or_404(get_user_model(),pk=user_pk)
    
    # 내가 상대방의 팔로워 목록에 잇다면
    if person.followers.filter(pk=request.user.pk).exists():
    # if request.user in person.followers.all():
        person.followers.remove(request.user)
    else:
        person.followers.add(request.user)
    return redirect('accounts:profile', person.username)
@require_POST
def follow(request,user_pk):
    if request.is_authenticated():
        person = get_object_or_404(get_user_model(),pk=user_pk)
        if request.user != person:
        # 내가 상대방의 팔로워 목록에 잇다면
            if person.followers.filter(pk=request.user.pk).exists():
            # if request.user in person.followers.all():
                person.followers.remove(request.user)
            else:
                person.followers.add(request.user)
        return redirect('accounts:profile', person.username)
    # 언팔로우
    # 없다면 팔로우
    else:
        return redirect('accounts:login')
**{% with followings=person.followings.all followers=person.followers.all  %}**
    <h1>{{person.username}} 의 프로필 페이지</h1>
    <div>
      <div> 팔로잉 수 : {{followings|length}}  / 팔로워 수 : {{followers|length}} </div>
    </div>
    {% if user != person %}
    <div>
      <form action="{% url 'accounts:follow' person.pk %}" method='POST'>
        {% csrf_token %}
        {% if user in followers %}
          <input type="submit" value='언팔로우'>
        {% else %}
          <input type="submit" value='팔로우'>
        {% endif %}
      </form>
    </div>
    
    {% endif %}
  {% endwith %}
  <hr>
profile
FE - devp

0개의 댓글