Django Model - 필드(filed) 모음집

정현우·2021년 12월 6일
4

Django Basic to Advanced

목록 보기
4/37
post-thumbnail

Djnago Model Field

장고 Model - ORM에서 사용하는 Field 리스트 살펴보기. 지속적으로 업데이트 중입니다! 오류 및 건의 사항은 댓글로 달아주세요! 장고 모델 - 필드 공식 문서 기준.
마지막 수정일: 23.03.31

  • Mysql 기점으로 Table - column의 자료구조와 매칭시키려 했으나, 혼용되는 부분도 있어 Django model - ORM 기준으로 정리했다.

숫자 관련 - Number

(Positive)IntegerField

class IntegerField(**options)

# ex
filed_name = IntegerField()

# chocies options 예시
class Card(models.Model):

    class Suit(models.IntegerChoices):
        DIAMOND = 1
        SPADE = 2
        HEART = 3
        CLUB = 4

    suit = models.IntegerField(choices=Suit.choices)
  • -2147483648에서 2147483647(4 Bytes)까지 정수 값을 저장한다.
  • MinValueValidator 및 MaxValueValidator를 사용하여 기본 데이터베이스가 지원하는 값을 기반으로 입력의 유효성을 검사한다. (-> Raises a ValidationError)
  • chocies options으로 Model내부에서 Model을 제정의 하는 방식으로도 많이 사용한다.
  • 접두사 Positive가 붙으면 음수 값은 Value Error가 나고, 무조건 0부터 시작한다.

(Positive)BigIntegerField

class BigIntegerField(**options)

# ex
filed_name = BigIntegerField()
  • -9223372036854775808에서 9223372036854775807(64비트 - 8 Bytes) 정수 값을 저장한다. Int의 2배 뙇
  • 접두사 Positive가 붙으면 음수 값은 Value Error가 나고, 무조건 0부터 시작한다.

(Positive)SmallIntegerField

class SmallIntegerField(**options)

# ex
filed_name = SmallIntegerField()
  • -32768에서 32767(2 Bytes)까지의 정수 값을 저장한다. Int의 1/2배 뙇
  • 접두사 Positive가 붙으면 음수 값은 Value Error가 나고, 무조건 0부터 시작한다.

(Big|Small)AutoField

class AutoField(**options)

# ex
filed_name = AutoField()
  • IntegerField와 기본적으로 동일하나, 자동적으로 증가하는 field이다. 즉, DBMS에서 특정 컬럼에 설정하는 auto increment에 해당하는 필드이다. (1부터 시작한다.)
  • 특정 field_name을 지징해서 AutoField로 만들어도 되지만, model에서 PK를 따로 설정하지 않으면 id를 AutoField로 자동적으로 만들어준다. 해당 정보를 장고 공식문서에서 보자
    • 장고 3.2 버전 부터는 AutoField가 아닌, BigAutoField 라고 한다.
    • AppConfig.default_auto_field 또는 globally(setting - conf) in the DEFAULT_AUTO_FIELD를 설정하지 않으면 자동적으로 만들어 준다.
  • Big은 1 to 9223372036854775807, Small은 1 to 32767 이다. 저장공간 관리, DB 튜닝을 할때 우린 해당 부분을 사소하게 넘기면 안될 것이다!

DecimalField

class DecimalField(max_digits=None, decimal_places=None, **options)

# ex
filed_name = DecimalField(max_digits=5, decimal_places=3)
  • 고정 소수점 이하의 십진수 실수를 저장한다. python의 Decimal instance를 사용한다.
  • 주로 "돈, 원화"에 해당하는, 소수점 계산 오차율범위를 거의 제로에 가깝게 해야하는 실수 필드에 사용한다.
  • max_digits : 숫자에 허용되는 최대 자릿수를 말하며, None이거나 decimal_places보다 크거나 같은 정수여야 한다.
  • decimal_places : 숫자와 함께 저장할 소수 자릿수를 말한다.
  • coerce_to_string
    • 표현식에 문자열 값을 반환해야하는 경우 True로 설정해야 한다.
    • Decimal 객체를 반환해야하는 경우 False로 설정해야 한다.
    • 기본값은 django.setting (conf)에서 COERCE_DECIMAL_TO_STRING 설정 키와 같은 값으로, 따로 오버라이드 하지 않으면 True 값을 가진다.
    • (DRF에서)serializer가 Decimal 객체를 반환하면 최종 출력 형식은 렌더러에 의해 결정된다! localize를 설정하면 값이 True로 설정 해야 한다.

FloatField

class FloatField(**options)

# ex
filed_name = FloatField()
  • 부동 소수점의 실수를 저장한다. python의 float instance를 사용한다. 고정 소수점과 부동소수점 차이는 CS 기초 중 하나이다. 컴퓨터는 2진수를 사용하고, 그에 따라서 소수점 표현이 굉장히 난해하다. 아직 잘 모른다면 꼭 짚고 넘어가자. 파이썬 공식문서도 도움이 된다.
  • 계산식에서 유동적인 연산이 필요한 부분에 float을 사용한다. 그래서 자연스럽게 model에서 decimal을 사용하는 경우가 많다고 한다.
  • 지수 형태로 표현되어서 아주 아주 큰 숫자 또는 아주 아주 작은 숫자를 표현 할때 장점이 많다. (과학적 계산 등이 아닌 이상, 일상 생활에서 이 정도 범위 큰 숫자를 사용하는 경우는 많지 않을 것 같다.)

BooleanField

class BooleanField(**options)

# ex
filed_name = BooleanField(default=False)
  • true/false 값을 저장하는 필드이다. 기본적으로 tinyint로 타입과 매핑된다(mysql기준).
  • 실제 boolean type대신 0,1 만 저장을 위한 tinyint가 메모리 적으로 유리하기 때문이다.
  • default값을 저장하지 않고, 옵션 값을 아무것도 안넣으면 None값이 default가 된다. 그래서 default를 써주는 것이 데이터 정확성을 올려준다.
  • 원래는 NullBooleanField 이었다. 3.1버전 이후로 페지되고 BooleanField가 되었다.

문자열 관련 - String

CharField

class CharField(max_length=None, **options)

# ex, 4글자 길이 제한
filed_name = CharField(max_length=4)

# inner class와 choice를 활용한 예시는 아래와 같다.
# 사실 정확하겐 java의 Enum과 비슷하게 사용하는 방법이다. (Enumeration types)
from django.utils.translation import gettext_lazy as _

class Student(models.Model):

    class YearInSchool(models.TextChoices):
        FRESHMAN = 'FR', _('Freshman')
        SOPHOMORE = 'SO', _('Sophomore')
        JUNIOR = 'JR', _('Junior')
        SENIOR = 'SR', _('Senior')
        GRADUATE = 'GR', _('Graduate')

    year_in_school = models.CharField(
        max_length=2,
        choices=YearInSchool.choices,
        default=YearInSchool.FRESHMAN,
    )

    def is_upperclass(self):
        return self.year_in_school in {
            self.YearInSchool.JUNIOR,
            self.YearInSchool.SENIOR,
        }
  • 가장 기본이되는 문자열 필드다. max_length로 최대 글자 길이를 제한한다. 긴 문자열을 입력하면 MaxLengthValidator가 ValidationError를 일으킨다.
  • django 3.2이후에 CharField.db_collation 라는 옵션 값이 추가되었다. data정렬 이름이다.
  • Student예시에서 Model inner - class model로 YearInSchool를 바로 만들어 버려서 CharField의 Choice를 제한하는 방법이 있다.
  • 추가로 models.Model에서 제공하는 함수를 def is_upperclass(self)와 같이 오버라이딩 해서 사용을 많이 한다. (특히 __str__ -> admin에서 보이는 값을 다르게 보여주기 때문)
filed_name = models.CharField(validators=[RegexValidator(regex='^.{4}$', message='Length has to be 4', code='nomatch')])
  • 위와 같이 validators와 RegexValidator를 활용해 fixed length를 설정해버릴 수 도 있다. 예시는 4글자 길이 제한을 보여준다.

TextField

class TextField(**options)

# ex
filed_name = TextField(max_length=4)
  • textarea에 사용되는 문자열 필드다. 길이 제한을 할꺼면 CharField가 훨씬낫다.
  • 캐리지리턴, 라인피드 등도 같이 저장되어 여러줄을 저장할 수 있다. CharField보다 많은 문자를 저장할 때 사용한다.

URLField

class URLField(max_length=200, **options)

# ex, max를 빼먹으면 기본적으로 200이 max
filed_name = URLField()
  • CharField와 동일하나, URLValidator를 사용한다. url 패턴에 매칭이되는 Regex(정규식)이 조건으로 걸려 있다.

EmailField

class EmailField(max_length=254, **options)

# ex, max를 빼먹으면 기본적으로 254이 max
filed_name = EmailField()
  • CharField와 동일하나, EmailValidator를 사용한다. url 패턴에 매칭이되는 Regex(정규식)이 조건으로 걸려 있다.
  • 해당 벨리데이션은 공식문서에서 옵션을 살펴보고 사용하는 게 좋다. django core mail을 사용할때 연동해서 체크해 볼 부분이 있다.
    • 해당 벨리데이션을 오버라이딩해 allowlist를 관리해 "허용된 이메일만" 가입하게 할 수 있다.

SlugField

class SlugField(max_length=50, **options)
  • 신문이나 잡지 등에서 제목을 쓸 때 중요한 의미를 포함하는 단어만을 이용해 제목을 작성하는 방법을 slug라고 한다.
  • django에서 slug란 글에서 보면 알 수 있듯이, 이미 확보된 데이터로부터 유효한 URL을 만드는 방법 이라고 한다.
  • max_length를 지정하지 않으면 기본적으로 50이다. 인덱싱(index)을 할 수 있다.
  • It is often useful to automatically prepopulate a SlugField based on the value of some other value. You can do this automatically in the admin using prepopulated_fields.
  • It uses validate_slug or validate_unicode_slug for validation.
  • SlugField.allow_unicode
    • If True, 아스키 코드 대신 유니코드 문자만 받는다. false가 default값이다.

이미 확보된 데이터로부터 유효한 URL을 만드는 방법 예시

from django.utils.text import slugify

class Article(models.Model):
    title = models.CharField(max_length=100)
    slug = models.SlugField(unique=True)

    def save(self, *args, **kwargs):
        self.slug = slugify(self.title)
        super(Article, self).save(*args, **kwargs)

# python manage.py shell
>>> test = Article.objects.create(title="django model field list")
>>> test.save()
>>> test.slug
"django-model-field-list"
  • Model의 save를 오버라이딩해서 object를 만들고 save할때 slug를 slugify를 활용해 자동으로 만들어서 저장하게 한다.
  • 누구나 해당 text를 이해하기 쉽다. 이 데이터를 활용해 특정 게시글의 접근 url로 만든다면? 즉 unique=True로 인덱싱을 하여 유일하게 만들어두고, url로 사용하면 된다.
    • 제목과 url의 의미론적 일치 -> 검색엔진 최적화 (SEO)에 도움도 된다.

GenericIPAddressField

class GenericIPAddressField(protocol='both', unpack_ipv4=False, **options)

# ex
filed_name = EmailField()
  • 문자열로 ip(IPv4, IPv6)형태를 저장하는 필드. All characters are converted to lowercase, IP의 공식 정규화 패턴을 따른다고 명시되어 있다.
  • GenericIPAddressField.protocol
    • default는 both값이다. IPv4, IPv6 값을 넣어 어떤 ip형태만 저장할지 정의할 수 있다.
  • GenericIPAddressField.unpack_ipv4
    • default는 false값이다. Can only be used when protocol is set to 'both'.
    • ::ffff:192.0.2.1 과 같은 IPv4 매핑 주소의 압축을 푼다. 이 옵션이 활성화되면 해당 주소는 192.0.2.1 로 압축이 풀린다.
  • 공백 값을 허용하면 공백 값이 널로 저장되므로 널값을 허용해야한다.

UUIDField

class UUIDField(**options)

# ex
import uuid
from django.db import models

class MyUUIDModel(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    # other fields
  • UUID는 Universal Unique Identifier이다. python에서 uuid obejct - 이 모듈은 RFC 4122에 명시된 대로 변경 불가능한 UUID 객체(UUID 클래스)와 버전 1, 3, 4, 5 UUID를 생성하기 위한 uuid1(), uuid3(), uuid4(), uuid5() 함수를 제공한다. python 공식문서 확인하기
  • uuid1, uuid4를 고유한 값 만들기로 사용한다.
    • 전자는 네트워크 주소를 기반, 후자는 완전한 랜덤값을 만든다. 네트워크 주소는 노출될 가능성이 있으니, 고유한 값 만들기로 후자를 추천한다.
  • 해당 object 기반으로 uuid를 만들고 관리하는 필드이다. postgresql을 사용하면 uuid타입으로 저장되고, 이외에는 char(32)로 저장된다.
  • AutoField대신 id값으로 많이 사용하며, db가 uuid를 자동으로 만들어 주지 않으니, default값을 uuid.uuid4를 활용해 만들어 주면 된다.
  • editable=False로 update 와 같은 수정작업을 불가능하게 하자.

휴대폰 번호, Phonenumber library

  • 다국적 phone number를 국가번호로 저장하고, 다양한 내장 mehtod를 활용하기 좋은 library가 있다. 바로 django-phonenumber-field 이다.
  1. pip install django-phonenumber-fieldpip install phonenumbers 로 설치한다. 그리고 INSTALLED_APPS'phonenumber_field'를 추가한다.

  2. 아래와 같이 model field를 세팅한다.

from django.db import models
from phonenumber_field.modelfields import PhoneNumberField

class Contact(models.Model):
	...
    phone_number = PhoneNumberField(
    	verbose_name="관리자 전화번호", 
        help_text="가게 관리자 전화번호입니다.", 
        # region='US' 이렇게 특정 리전 고정도 가능
		unique=True,
    )
  1. 다양한 method 중 as_national_number 를 사용하면 아래와 같다.
contact = Contact.objects.create(name='John', phone_number='+14155552671')
national_number = contact.phone_number.as_national_number() # returns '4155552671'
  1. official docs. Model뿐 아니라 Form, Serializer 에도 활용가능하다.

날짜 관련

DateField

class DateField(auto_now=False, auto_now_add=False, **options)

# ex
created_at = models.DateField(auto_now_add=True)
updated_at = models.DateField(auto_now=True)
  • 기본적으로 Python by a datetime.date 인스턴스(object)기반으로 만들어지는 date관련 필드다.
  • DateField.auto_now
    • auto_now를 True로 주면 save될 때 마다 자동으로 업데이트 된다.
    • 하지만 QuerySet으로 접근해 object를 직접적으로 update하는 경우에는 업데이트 되지 않는다. django에서 보통 update할때 save를 사용하며 그에따라 update_at 처럼 마지막 수정날짜를 보관하는데에 좋다.
  • DateField.auto_now_add
    • auto_now_add를 True로 주면 처음 created될 때 날짜를 저장한다. 그래서 DB row 생성일, 특히 데이터 created_at에 많이 활용이 된다. DateField와 DateTimeField의 default 설정 기본값이 상이하니 꼭 체크하고 사용해야 한다.
    • DateField: default=date.today - from datetime.date.today()
    • DateTimeField: default=timezone.now - from django.utils.timezone.now()
  • setting auto_now or auto_now_add to True will cause the field to have editable=False and blank=True set.

DateTimeField

class DateTimeField(auto_now=False, auto_now_add=False, **options)

# ex
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
  • 기본적인 설정 값은 DateField와 동일하다. created_at, updated_at 사용법도 동일하다.
  • 하지만 datetime.datetime 인스턴스(object)기반으로 만들어지는 것에 차이점이 있다. -> 시간 까지 저장된다. (ex - 2021-12-04 04:48:34.018911)

DurationField

class DurationField(**options)

# ex
time_passed = models.DurationField(default=timedelta())
  • Python by timedelta 인스턴스 기반으로 만들어 진다. 그렇기 때문에 timedelta에 익숙해야 해당 object를 용이하게 사용할 수 있다. python 공식문서에서 해당 object를 어떻게 다루는지 체크해보자.
  • 해당 컬럼은 어떤 DBMS를 사용하느냐에 따라 데이터 타입이 상이하다.
    • PostgreSQL의 경우 해당 컬럼 타입은 "interval" 로 만들어진다.
    • Oracle의 경우 해당 컬럼 타입은 INTERVAL DAY(9) TO SECOND(6) 로 만들어진다.
    • 그 외의 경우 일반적으로 bigint 로 선언 되어 microseconds값 이 저장된다.
  • 예시에서 같이 default값을 설정해서 "얼마나 시간이 지낫는지"로 사용 가능하다.

파일 관련

  • 파일 관련된 처리는 django 내부에서 추상화 되어 있는 부분이 많다.
  • 사용하기 전 공식문서 File storage API를 읽어보고 사용하는 것을 추천한다.
  • 특히 파일 관련 된 부분을 AWS의 S3와 같은 CDN 서버를 활용할 것이면 model 설계 단계에서 부터 고려하여 출발하는 것이 좋다. -> 왜냐하면 해당하는 Storage Class를 오버라이딩 해서 설정해주고 model단에서 사용하는 것이 django에서 기본적으로 스토리지 서버를 활용하는 것으로 가이드를 하기 때문이다.
    • s3를 도입할 생각이 있다면, 해당 글을 확인해 보자. 그리고 공식문서도 있다.
    • s3boto3 라는 모듈이 핵심이고, 해당 모듈을 활용해 Storag class를 오버라이딩 해서 해당 class를 스토리지에 사용하도록 하게 한다.
  • 필자는 레거시의 file 처리 부분을 S3로 돌리기에 당장 영향범위가 너무 넓어서 포기를 고민한다... 코딩은 설계가 80%라는 말이 점점 동감된다..
  • 그리고 파일을 다뤄야하는 부분은 XSS or CSRF attacks 와 같은 보안 문제를 동시에 꼭 고민해야한다.

FileField

class FileField(upload_to=None, max_length=100, **options)

# ex
# file will be uploaded to MEDIA_ROOT/uploads
upload = models.FileField(upload_to='uploads/')

# or...
# file will be saved to MEDIA_ROOT/uploads/2015/01/30
upload = models.FileField(upload_to='uploads/%Y/%m/%d/')
  • 파일 upload 디렉토리와 파일 이름 저장 용도로 사용하며 options으로 pk를 제공하지 않는다 -> 즉, 기본키 설정을 할 수 없다.
  • max_length에서 예상할 수 있듯이 varchar(mysql기준) 데이터 타입으로 컬럼이 생성된다.
  • 모델이 만들어지고 FileField의 값은 Storage.save() 메소드로 pass가 되어 진다.
  • default FileSystemStorage을 사용하면 django setting (conf)로 설정한 MEDIA_ROOT path로 저장 location이 세팅 된다.
  • DRF를 사용한다면 view단에서 parser_classes = (MultiPartParser,) 와 같이 MultiPart Form으로 request를 받아야 한다.
def user_directory_path(instance, filename):
    # file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
    return 'user_{0}/{1}'.format(instance.user.id, filename)

class MyModel(models.Model):
    upload = models.FileField(upload_to=user_directory_path)
  • 위 예시에서 와 같이 upload_to에 함수를 할당해서 활용할 수 있다. 코드의 불필요한 반복이 줄어들고, file을 사용하는 부분의 upload_to를 따로 빼내서 관리가 가능하다. -> 변동 사항에 유동적으로 대응이 가능하다.

  • ps) For example, say your MEDIA_ROOT is set to '/home/media', and upload_to is set to 'photos/%Y/%m/%d'. The '%Y/%m/%d' part of upload_to is strftime() formatting. '%Y' is the four-digit year, '%m' is the two-digit month and '%d' is the two-digit day. If you upload a file on Jan. 15, 2007, it will be saved in the directory /home/media/photos/2007/01/15.

  • ps) 업로드된 파일이 수정가능하고 re-size를 하게 하려면 각각의 name과 size를 컬럼으로 만들어 저장하는 것을 추천한다. 그리고 option이 많으니 꼭 공식문서에서 더 자세하게 확인하자!!

FilePathField

class FilePathField(
	path='', match=None, recursive=False, 
	allow_files=True, allow_folders=False, 
	max_length=100, **options
)

# ex
import os
from django.conf import settings
from django.db import models

def images_path():
    return os.path.join(settings.LOCAL_FILE_DIR, 'images')

class MyModel(models.Model):
    file = models.FilePathField(path=images_path)
  • CharField 기반으로, 파일 시스템에서 특정 디렉토리에 파일 이름으로 제한(벨리데이션)한다. path가 필수 인자고 절대경로를 의미한다.
  • match
    • FilePathField가 파일이름을 필터링할 때 사용할 문자열로 된 정규표현식을 의밓ㄴ다. 정규표현식은 전체 경로가 아닌 기본 파일이름에 적용된다.
  • recursive - True/False (default = False).
    • path의 모든 서브 디렉토리가 포함되어야하는지 여부를 체크한다.
  • allow_files - True/False (default = True).
    • 지정된 위치에 파일을 포함할지 여부
    • allow_files, allow_folders 둘 중 하나는 True여야 한다.
  • allow_folders - True/False (default = True).
    • 지정된 위치에 폴더를 포함할지 여부

FilePathField(path="/home/images", match="foo.*", recursive=True)

  • will match /home/images/foo.png but not /home/images/foo/bar.png because the match applies to the base filename (foo.png and bar.png).

  • FilePathField instances are created in your database as varchar columns with a default max length of 100 characters. -> As with other fields, you can change the maximum length using the max_length argument.

ImageField

class ImageField(upload_to=None, height_field=None, width_field=None, max_length=100, **options)# ex
photo = models.ImageField(upload_to='uploads/%Y/%m/%d/', blank=True)
  • FileField의 모든 속성을 상속받은 필드이다. varchar columns으로 만들어 지고, 기본적으로 100자의 길이 제한을 가진다. 파일이긴 하지만 "이미지"에만 초점이 맞춰진 필드이다. FileField를 잘 알아야 더 잘 활용이 가능한 필드다.
  • 위 코드 예시에서 photo 컬럼은 공백이 가능하며 이미지를 저장할 수 있다. FileField 예시와 동일하게 설정하면, MEDIA_ROOT/uploads/2021/12/05 와 같은 디렉토리에 저장된다.
  • FileField에는 없는 height, width 속성을 사용할 수 있다.
  • ImageField.height_field
    • 해당 인스턴스가 save될 때 height값이 자동으로 저장되어 접근해서 값을 가져올 수 있다.
  • ImageField.width_field
    • 높이와 동일하게, 해당 인스턴스가 save될 때 width값이 자동으로 저장되어 접근해서 값을 가져올 수 있다.

Others

JSONField

class JSONField(encoder=None, decoder=None, **options)

# ex
data = models.JSONField(default='{}')

# 함수를 활용한 예시
def contact_default():
    return {"email": "to1@example.com"}

contact_info = JSONField("ContactInfo", default=contact_default)
  • 기본적으로 JSON 인코딩 데이터를 저장하기 위한 필드다. 쉽게말하면 python의 dict로 캐스팅(변환) 과정 없이 바로 사용가능한 json field를 만들 수 있다.
  • JSONField is supported on MariaDB 10.2.7+, MySQL 5.7.8+, Oracle, PostgreSQL, and SQLite (with the JSON1 extension enabled).
  • JSONField.encoder
    • 데이터를 직렬화(python obejct to json) 하기 위해 기본 JSON 시리얼라이저가 아닌 json.JSONEncoder를 사용하게 할 수 있다.
    • 예를 들어 DjangoJSONEncoder와 같은 class 말이다.
  • JSONField.decoder
    • 인코더에 대한 설명과 거의 동일하다. (디코더는 json to python object 이다.)
    • 예를 들어 datetime 값을 저장해 뒀는데 이게 실질적으로는 string 일때 문제가 발생한다. 그런 부분을 핸들링 하기 위해 decoder를 세팅할 수 있다.
  • 해당 필드 indexing 처리에 대한 django의 견해는 "Index and Field.db_index both create a B-tree index, which isn’t particularly helpful when querying JSONField. On PostgreSQL only, you can use GinIndex that is better suited." 와 같다.
  • filter등을 사용하여 여러가지 형태의 질의(query)를 던질 수 있다. 아래에서 조금 더 자세하게 살펴보자!

JSONField Query

  • 공식 문서에서 JSONField에 대한 다양한 질의가 정리되어 있다.
  • 다양한 형태의 질의가 잦은 CharField대상으로 JSONField도 고려해보는 것 도 좋을 것 같다.
    • 가령 예를 들면 게시글 내용을 json으로 저장해 key값을 title(slug), content, footer ..등 으로 저장해 검색 최적화로 사용한다던가 (질의를 key에 접근해서 한 줄로 가능하기 때문) 말이다. 아래 예시들을 잘 살펴보자
from django.db import models

class Dog(models.Model):
    name = models.CharField(max_length=200)
    data = models.JSONField(null=True)

    def __str__(self):
        return self.name
  • 정렬과 None 필터링 (or None 찾기)
    • None와 Value('null')의 다름. 전자는 SQL에서 NULL, 후자는 JSON scalar null을 의미한다. django는 후자를 추천한다.
>>> Dog.objects.create(name='Max', data=None)  # SQL NULL.
<Dog: Max>
>>> Dog.objects.create(name='Archie', data=Value('null'))  # JSON null.
<Dog: Archie>
>>> Dog.objects.filter(data=None)
<QuerySet [<Dog: Archie>]>
>>> Dog.objects.filter(data=Value('null'))
<QuerySet [<Dog: Archie>]>
>>> Dog.objects.filter(data__isnull=True)
<QuerySet [<Dog: Max>]>
>>> Dog.objects.filter(data__isnull=False)
<QuerySet [<Dog: Archie>]>
  • None과 Value('null')을 저장한 두 object, model대상으로 filter로 None을 찾아보면 SQL NULL값은 찾아지지 않는다. 컬럼이름__isnull(sql에서 사용한는 isnull 쿼리를 하는 것)을 통해서 해당 Null을 찾을 수 있다.

    • 즉, sql null과 value('null')이 전혀 다르게 필터링 된다.
  • SQL NULL 값으로 작업하려는 경우가 아니라면 null=False를 설정하고 빈 값에 적절한 기본값(예: default=default)을 제공하는 것이 좋다.

>>> Dog.objects.create(name='Rufus', data={
...     'breed': 'labrador',
...     'owner': {
...         'name': 'Bob',
...         'other_pets': [{
...             'name': 'Fishy',
...         }],
...     },
... })
<Dog: Rufus>

>>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': None})
<Dog: Meg>

>>> Dog.objects.filter(data__breed='collie')
<QuerySet [<Dog: Meg>]>

>>> Dog.objects.filter(data__owner__name='Bob')
<QuerySet [<Dog: Rufus>]>

>>> Dog.objects.filter(data__owner__other_pets__0__name='Fishy')
<QuerySet [<Dog: Rufus>]>

>>> Dog.objects.create(name='Shep', data={'breed': 'collie'})
<Dog: Shep>

>>> Dog.objects.filter(data__owner__isnull=True)
<QuerySet [<Dog: Shep>]>
  • 위 예시들에서 filter로 값을 전달하는 형태를 살펴보자! 기본적으로 data 컬럼이름 으로 접근하고, 관계 있는 model에 대한 컬럼 참조할때 와 같이, depth있게 언더바 두개( __ )로 하위 key에 접근을 할 수 있다. -> 더 깊이 있게 참조 가능
  • array의 경우 언더바 두개 뒤에 index를 붙여서 접근을 할 수 있다. 위 예시들은 정확한(exact)값을 찾는 것이다.
  • Can also be chained with(언더바 2개 사용은 동일하게): icontains, endswith, iendswith, iexact, regex, iregex, startswith, istartswith, lt, lte, gt, and gte, as well as with Containment and key lookups(위 예시 형식이 Containment and key lookups임)
# contains
>>> Dog.objects.create(name='Rufus', data={'breed': 'labrador', 'owner': 'Bob'})
<Dog: Rufus>
>>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': 'Bob'})
<Dog: Meg>
>>> Dog.objects.create(name='Fred', data={})
<Dog: Fred>
>>> Dog.objects.filter(data__contains={'owner': 'Bob'})
<QuerySet [<Dog: Rufus>, <Dog: Meg>]>
>>> Dog.objects.filter(data__contains={'breed': 'collie'})
<QuerySet [<Dog: Meg>]>

# contained_by
>>> Dog.objects.create(name='Rufus', data={'breed': 'labrador', 'owner': 'Bob'})
<Dog: Rufus>
>>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': 'Bob'})
<Dog: Meg>
>>> Dog.objects.create(name='Fred', data={})
<Dog: Fred>
>>> Dog.objects.filter(data__contained_by={'breed': 'collie', 'owner': 'Bob'})
<QuerySet [<Dog: Meg>, <Dog: Fred>]>
>>> Dog.objects.filter(data__contained_by={'breed': 'collie'})
<QuerySet [<Dog: Fred>]>

# has_key
>>> Dog.objects.create(name='Rufus', data={'breed': 'labrador'})
<Dog: Rufus>
>>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': 'Bob'})
<Dog: Meg>
>>> Dog.objects.filter(data__has_key='owner')
<QuerySet [<Dog: Meg>]>

# has_keys
>>> Dog.objects.create(name='Rufus', data={'breed': 'labrador'})
<Dog: Rufus>
>>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': 'Bob'})
<Dog: Meg>
>>> Dog.objects.filter(data__has_keys=['breed', 'owner'])
<QuerySet [<Dog: Meg>]>

# has_any_keys
>>> Dog.objects.create(name='Rufus', data={'breed': 'labrador'})
<Dog: Rufus>
>>> Dog.objects.create(name='Meg', data={'owner': 'Bob'})
<Dog: Meg>
>>> Dog.objects.filter(data__has_any_keys=['owner', 'breed'])
<QuerySet [<Dog: Rufus>, <Dog: Meg>]>
  • 다중 query를 엮어 주는 Q from django.db.models import Q를 활용하는 방법은 공식문서에서 더 자세하게 살펴보자.

BinaryField

class BinaryField(max_length=None, **options)

# ex
class GeeksModel(Model):
    geeks_field = models.BinaryField()

# creating a bytes object
res = bytes(test_string, 'utf-8')
# creating a instance of
# GeeksModel
geek_object = GeeksModel.objects.create(geeks_field = res)
geek_object.save()
  • python 에서 제공하는 instance - bytes, bytearray, or memoryview 와 같은 raw 바이너리 데이터를 저장하는 특수 필드이다.
  • 예시와 같이 사용이 가능하다. 자세한 사항은 예시 페이지를 살펴보자
  • 하지만 해당 필드를 파일 저장에 쓰는 것에 단점이 있다. 정리한 글을 살펴보자

ArrayField

  • PostgreSQL 전용 model fields!!
class ArrayField(base_field, size=None, **options)[source]

# ex
from django.contrib.postgres.fields import ArrayField
from django.db import models

class ChessBoard(models.Model):
    board = ArrayField(
        ArrayField(
            models.CharField(max_length=10, blank=True),
            size=8,
        ),
        size=8,
    )
  • 사용 해본 경험이 전무해 공식 문서를 추천한다.
profile
도메인 중심의 개발, 깊이의 가치를 이해하고 “문제 해결” 에 몰두하는 개발자가 되고싶습니다. 그러기 위해 항상 새로운 것에 도전하고 노력하는 개발자가 되고 싶습니다!

0개의 댓글