티스토리 뷰

Python/* Django

9. Model Relationship Fields

포꾼 2017. 12. 13. 15:07

Model Relationship Fields

포스팅과 댓글, 포스팅과 글쓴이, 포스팅과 카테고리 등의 정보를 RDBMS (관계형 데이터베이스, Relational Database management System)

에 저장하기 위해서는, Relation에 대한 이해가 필요 관계가 있는 Record끼지 서로 연결(Link)


데이터베이스 정규화 

• 정규화 : RDBMS 설꼐에서 중복을 최소화하게 데이터를 구조화하는 프로세스

• 충분히 정규화하지 않는다면, 중복 정보가 복수 개의 Row/Column에 혼재 : Record 갱신/삭제 시에 관련 Row/Column에 대해서 처리되지 않을 경우, 논리적 모순 발생

• 경우에 따라 비정규화 과정이 필요


1:N - 포스팅과 댓글

# blog/models.py


from django.db import models


class Post(models.Model):

title = models.CharField(max_length=100)

content = models.TextField()


class Comment(models.Model): ### 1:N 

       post = models.ForeignKey(Post)       # post_id라는 필드로 생성이 된다.

       author = models.CharField(max_length=20)

       message = models.TextField()

       create_at = models.DateTimeField(auto_now_add=True)

       update_at = models.DateTimeField(auto_now=True)

Comment라는 모델 클레스를 생성하였으므로, 마이그레이션 작업을 진행 한다

> python3 manage.py makemigrations blog

> python3 manage.py migrate blog


admin 페이지 등록

# blog/admin.py


@admin.register(Comment)

class CommentAdmin(admin.ModelAdmin):

       pass


글 클릭 시 댓글 조회 가능하도록 설정

# blog/templates/blog/post_detail.html


<h3>Comments</h3>

    <ul>

    {% for comment in post.comment_set.all %}

<li>

            {{ comment.message }}

            <smal>by{{ comment.author}}</smal>

            <small>at {{ comment.update_at }} </small>

        </li>

    {% endfor %}

</ul>

‘옵션 열기’ 누가 남긴 흔적일까…

‘옵션 열기’는 누가 남긴 흔적일까. 인터네상 정황을 종합하면 ‘정치 성향이 뚜렷하지만 컴퓨터나 스마트폰 활용에 미숙한 포털 사이트 및 소셜네트워크서비스(SNS) 회원’ 정도로 압축할 수 있다. 특정 정치세력의 ‘댓글 부대’라는 의심이 나오는 이유는 여기에 있다.

‘옵션 열기’는 지난 5일 한 트위터 이용자가 포착한 인터넷 포털 사이트 댓글을 계기로 주목을 끌었다. 이 이용자는 비정규직 공무원의 정규직 전환을 다룬 인터넷뉴스 댓글 게시판에서 “옵션 열기 공무원수 늘리면 나라 2년 안에 망하고 최저 임금 올리면 1년 안에 망한다. 법인세 인상하면 3년 안에 망한다. 그런데 문재인 이 인간은 이 3개를 모두 같이 한다. 대한민국 끝났다”는 내용의 댓글을 발견했다.

댓글 도입부에 등장한 ‘옵션 열기’는 문맥상 어색했다. 그 아래에는 “어디서 복사했기에, 옵션 열기 공무원은 뭐야”라는 추가 댓글이 붙었다. 이 댓글을 소개한 트위터 이용자는 “정말 웃긴다. ‘댓글 부대’가 급했는지 내용을 확인하지도 않고 내용을 복사해 붙였다”고 했다. 이 이용자의 게시물은 이틀 사이에 2500회 이상 재배포됐고, 여러 커뮤니티 사이트로 퍼졌다.

‘옵션 열기’는 누군가가 게시글을 반복적으로 유포할 목적으로 복사해 붙여넣는 과정에서 실수로 남긴 흔적이라는 추측에 무게가 실리고 있다. 다만 ‘옵션 열기’가 어느 전자기기의 기능을 나타낸 문구인지, 게시판의 항목을 나타낸 문구인지 확인되지는 않았다.

Comments

  • 정말 그러한가요?ㅎㅎ by키보드워리어 at Dec. 8, 2017, 1:21 p.m.


M:N - Relation없이 포스팅과 태그

# blog/models.py


class Post(models.Model):

    tag_set = models.ManyToManyField('Tag', blank=True) # 릴레이이션을 지정할때 클레스를 지정, Tag의 모델은 하위에 있으므로 문자열 처리

                                                                               (문자열로 릴레이션 지정도 가능하다)


class Tag(models.Model): ### M : N

    name = models.CharField(max_length=50, unique=True)


    def __str__(self):   # admin 페이지 내 태그 명으로 출력하기 위함

        return self.name

Tag라는 모델 클레스를 생성하였으므로, 마이그레이션 작업을 진행 한다

> python3 manage.py makemigrations blog

> python3 manage.py migrate blog


admin 페이지 등록

# blog/admin.py


@admin.register(Tag)

class TagAdmin(admin.ModelAdmin):

       pass


## 사전에 어드민 페이지를 통해 여러 블로그에 태그를 지정하였다.


태그지정 확인

Post.objects.filter(tag_set__name='django') ## 태그이름이 django인 글을 출력

<QuerySet [<Post: 제목 #244>]>


Post.objects.filter(tag_set__name='askdjango')   ## 태그이름이 aksdjango인 글을 출력

<QuerySet [<Post: 제목 #245>, <Post: 제목 #243>]>


Post.objects.filter(tag_set__name__in=['askdjango','python'])  ## 태그이름이 aksjdano이거나, python 인 글을 출력

<QuerySet [<Post: 제목>, <Post: 제목 #245>, <Post: 제목 #245>, <Post: 제목 #244>, <Post: 제목 #243>]> 

ManyToManyField를 정의하면 blog_post_tag_set이라는 테이블이 추가로 생성 된다.

# blog_post_tag_set 신규테이블


"ID"   "post_id  "tag_id"

"1" "250"        "1"

"2" "250"        "3"

"3" "249"        "1"

"4" "249"        "2"

"5" "248"        "3"

"6" "251"        "1" 



1:1 - User와 Profile

Django 에서는 django.contrib.auth.models.User 모델을 기본 제공

User에 대한 부가적인 정보(전화번호, 주소 등)를 저장하기 위해, Profile 모델을 1:1 관계로 설계 가능


profile 이란 모델을 생성하기위해, accounts라는 앱을 생성한다

> python3 manage.py startapp accounts

(중요) 반드시 추가 앱을 생성 할 경우 settings.py, urls.py를 정의 및 생성 해준다.

class Profile(models.Model): ## 1:1

    #user = models.OneToOneField(User) # FIXME: BADE CASE !!!

    user = models.OneToOneField(settings.AUTH_USER_MODEL)

    phone_number = models.CharField(max_length=20)

    address = models.CharField(max_length=50)

Profile라는 모델 클레스를 생성하였으므로, 마이그레이션 작업을 진행 한다

> python3 manage.py makemigrations accounts

> python3 manage.py migrate accounts


admin 페이지 등록

# accou/admin.py


from django.contrib import admin

from .models import Profile


@admin.register(Profile)

class ProfileAdmin(admin.ModelAdmin):

       pass




ForeignKey 와의 차이

생성되는 필드명은 같으나 유일성의 차이

class Post(models.Model):

    user = models.ForeignKey(User) # 한명의 유저는 다수의 Post를 가진다.

    # 필드 SQL: "user_id" integer NOT NULL REFERENCES "auth_user" ("id")


class Profile(models.Model):

    user = models.OneToOneField(User)  # 한명의 유저는 유일한 Profile을 가진다.

    # 필드 SQL: "user_id" integer NOT NULL UNIQUE REFERENCES "auth_user" ("id") 


참고 : auth.User 모델과 관계를 맺을 때

from django.conf import settings

from django.contrib.auth.models import User


# 방법1) 비추천

user = models.OneToOneField(User)


# 방법2) 비추천

user = models.OneToOneField('auth.User')


# 방법3) 추천

user = models.OneToOneField(settings.AUTH_USER_MODEL)

• 장고 사용자 인증에 사용되는 User모델 변경을 지원 


ForeignKey.on_delete 옵션

1측의 Row가 삭제될 경우, N측의 Row의 처리에 대한 동작을 지정

• CASCADE : 연결된 Row 를 일괄 삭제 (디폴트 동작) # 포스팅이 삭제되면 댓글은 다 삭제된다.

• PROTECT : ProtectedError 예외를 발생시키며, 삭제 방지 # 삭제 금지, 포스트 레코드 삭제 금지

• SET_NULL : null=True 설정이 되어있을 때, 삭제되면 해당 필드를 null 설정 # 외래키 필드 NULL 지정

• SET_DEFAULT : 필드에 지정된 디폴트값으로 설정  # 외래키 default값을 지정해놨으면, default로 지정

• SET : 값이나 함수를 지정. 함수의 경우 호출결과값을 지정

• DO_NOTHING : 대개의 DB에서는 오류발생의 가능성 존재. sqlite3는 엄격하지 않음.


ForeignKey에서 related_name 지정의 필요성

1:N 관계에서 1측에서 N측으로 접근 시의 속성명 : 모델명소문자_set


# blog/models.py

class Post(models.Model):

user = models.ForeignKey(settings.AUTH_USER_MODEL)

이때, 특정 user_instance의 Post목록

• blog.models.Post.objects.filter(user=user_instance)

• user_instance.post_set.all()

> post = Post.objects.first()

> print (Comment.objects.filter(post=post))

> print (post.commnet_set.all()) 


related_name 이름 중복이 발생 ## blog앱에도 Post라는 모델이 존재, shop앱에도 Post라는 모델이 존재 , post_set을 가지려고 하기떄문에 충돌이 발생

• user_instance.post_set 은 어떤 앱 (blog/shop) 의 Post인가?

• related_name이 중복되지 않도록 지정을 해야만, makemigrations 명령이 동작

# blog/models.py

class Post(models.Model):

user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='blog_post_set')

# shop/models.py  ## shop app을 생성해서 아래와같이 정의해라.

class Post(models.Model):

user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='shop_post_set')

• 혹은 related_name을 쓰지 않도록 지정도 가능 : "+"  ## 릴레이티드 네임을 포기 한다.  ## 한쪽을 포기하던지, 중복을 피하던지..

# shop.models.Post.objects.filter(user=user)



갯수 카운트하는 2가지 방법

from blog.models import Post

from django.db import connection

# 방법1) len(QuerySet) - 모든 Record를 메모리에 로드하여, 카운트

# 'SELECT "blog_post"."id", "blog_post"."title", "blog_post"."content",

# "blog_post"."tags", "blog_post"."lnglat", "blog_post"."created_at",

# "blog_post"."test_field" FROM "blog_post"

print(len(Post.objects.all()))

# 방법2) QuerySet.count() - 해당 Record갯수를 DB에게 질의

# 'SELECT COUNT(*) AS "__count" FROM "blog_post"

print(Post.objects.all().count()) 




"""본 내용은 AskDjango VOD, "장고 차근차근 시작하기" 강의내용을 참고하여 작성했습니다.(https://nomade.kr/)"""

반응형

'Python > * Django' 카테고리의 다른 글

11. Django Template Loader  (0) 2017.12.13
10. Django 템플릿 상속  (0) 2017.12.13
8. Http Status Code 404  (0) 2017.12.13
7. 모델을 통한 데이터 CRUD  (0) 2017.12.12
6. Django Admin  (0) 2017.12.12
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함