풀스택 개발 공부로그

Django ORM create, save

|

Django ORM (create, save)

Models and databases | Django documentation | Django

A model is the single, definitive source of data about your data. It contains the essential fields and behaviors of the data you’re storing. Generally, each model maps to a single database table.

모델은 데이터의 필드와 동작을 가지고있다. 모델은 데이터 베이스의 각 테이블과 매칭된다.

Model

Making Queries

Once you’ve created your data models , Django automatically gives you a database-abstraction API that lets you create, retrieve, update and delete objects. This document explains how to use this API. Refer to the data model reference for full details of all the various model lookup options.

일단 데이터 모델을 생성하면 장고에서 자동으로 데이터베이스 추상화 API를 제공한다. 이는 객체를 생성, 조회, 업데이트, 삭제를 할 수 있다.

Throughout this guide (and in the reference), we’ll refer to the following models, which comprise a Weblog application:

from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def __str__(self):
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=200)
    email = models.EmailField()

    def __str__(self):
        return self.name

class Entry(models.Model):
    blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
    headline = models.CharField(max_length=255)
    body_text = models.TextField()
    pub_date = models.DateField()
    mod_date = models.DateField()
    authors = models.ManyToManyField(Author)
    number_of_comments = models.IntegerField()
    number_of_pingbacks = models.IntegerField()
    rating = models.IntegerField()

    def __str__(self):
        return self.headline

Creating objects

To represent database-table data in Python objects, Django uses an intuitive system: A model class represents a database table, and an instance of that class represents a particular record in the database table. To create an object, instantiate it using keyword arguments to the model class, then call save() to save it to the database. Assuming models live in a file mysite/blog/models.py, here’s an example:

키워드 인자를 사용하여 인스턴스화한 뒤 save()를 호출하여 디비에 저장한다.

>>> from blog.models import Blog
>>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
>>> b.save()

This performs an INSERT SQL statement behind the scenes. Django doesn’t hit the database until you explicitly call save() .

이는 insert SQL을 수행한다. save를 호출하기 전까지 데이터베이스와 통신하지 않는다.

Overriding predefined model methods

Models | Django documentation | Django

There’s another set of model methods that encapsulate a bunch of database behavior that you’ll want to customize. In particular you’ll often want to change the way save() and delete() work.

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def save(self, *args, **kwargs):
        do_something()
        super().save(*args, **kwargs)  # Call the "real" save() method.
        do_something_else()

It’s important to remember to call the superclass method – that’s that super().save(*args, **kwargs) business – to ensure that the object still gets saved into the database. If you forget to call the superclass method, the default behavior won’t happen and the database won’t get touched.

super().save()를 호출하지 않으면 원래 save()가 제대로 작동하지 않는다. 꼭 하길.

Overridden model methods are not called on bulk operations

Note that the delete() method for an object is not necessarily called when deleting objects in bulk using a QuerySet or as a result of a cascading delete . To ensure customized delete logic gets executed, you can use pre_delete and/or post_delete signals. Unfortunately, there isn’t a workaround when creating or updating objects in bulk, since none of save() , pre_save , and post_save are called.

하지만 대량의 작업에서는 재정의된 함수가 호출되지 않는다. 가능하면 커스텀하지 말아야겠다.

Saving changes to objects

Making queries | Django documentation | Django

To save changes to an object that’s already in the database, use save() . Given a Blog instance b5 that has already been saved to the database, this example changes its name and updates its record in the database:

디비에 있는 객체를 바꿀 때 save()사용.

>>> b5.name = 'New name'
>>> b5.save()

This performs an UPDATE SQL statement behind the scenes. Django doesn’t hit the database until you explicitly call save() . update역할을 한다.

Saving ForeignKey and ManyToManyField fields

Making queries | Django documentation | Django

Foreign Key

Updating a ForeignKey field works exactly the same way as saving a normal field – simply assign an object of the right type to the field in question. This example updates the blog attribute of an Entry instance entry, assuming appropriate instances of Entry and Blog are already saved to the database (so we can retrieve them below):

FK update도 일반적인 update와 동일하다. 다음은 Entry 인스턴스의 blog 속성을 업데이트하는 예이다.

>>> from blog.models import Blog, Entry
>>> entry = Entry.objects.get(pk=1)
>>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
>>> entry.blog = cheese_blog
>>> entry.save()

ManyToMany

Updating a ManyToManyField works a little differently – use the add() method on the field to add a record to the relation. This example adds the Author instance joe to the entry object:

M2M는 조금 다르다. add()를 사용한다.

>>> from blog.models import Author
>>> joe = Author.objects.create(name="Joe")
>>> entry.authors.add(joe)

자세한건 여기서 : Related objects reference | Django documentation | Django

To add multiple records to a ManyToManyField in one go, include multiple arguments in the call to add() , like this:

M2M필드에서 여러 인스턴스를 한번에 저장하려면 다음과같이

>>> john = Author.objects.create(name="John")
>>> paul = Author.objects.create(name="Paul")
>>> george = Author.objects.create(name="George")
>>> ringo = Author.objects.create(name="Ringo")
>>> entry.authors.add(john, paul, george, ringo)

CSS grid vs flex

|

Django Restframework View

|

Django Restframework View

Views - Django REST framework

REST framework provides an APIView class, which subclasses Django’s View class. APIView classes are different from regular View classes in the following ways:

  • Requests passed to the handler methods will be REST framework’s Request instances, not Django’s HttpRequest instances.
  • Handler methods may return REST framework’s Response, instead of Django’s HttpResponse. The view will manage content negotiation and setting the correct renderer on the response.
  • Any APIException exceptions will be caught and mediated into appropriate responses.
  • Incoming requests will be authenticated and appropriate permission and/or throttle checks will be run before dispatching the request to the handler method.
  • handler methods를 통과하는 요청은 Django의 HttpResquest가 아닌 DRF의 리퀘스트 인스턴스가 된다.
  • responser도 설정할 수 있다.
  • 요청이 들어오면 이를 handler method로 전달하기 전에 적절한 권한을 부여하고 throttle(호출 횟수 제한) 등을 얼마나 줄지 체크합니다.

Using the APIView class is pretty much the same as using a regular View class, as usual, the incoming request is dispatched to an appropriate handler method such as .get() or .post(). Additionally, a number of attributes may be set on the class that control various aspects of the API policy.

APIView는 리퀘스트를 받아 적절한 hadler method( .get() or .post() 등)에 전달하는 일반적인 view와 상당히 유사하다.

class ListUsers(APIView):
    """
    View to list all users in the system.

    * Requires token authentication.
    * Only admin users are able to access this view.
    """
    authentication_classes = [authentication.TokenAuthentication]
    permission_classes = [permissions.IsAdminUser]

    def get(self, request, format=None):
        """
        Return a list of all users.
        """
        usernames = [user.username for user in User.objects.all()]
        return Response(usernames)

Typically when using the generic views, you’ll override the view, and set several class attributes.

class UserList(generics.ListCreateAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [IsAdminUser]

    def list(self, request):
        # Note the use of `get_queryset()` instead of `self.queryset`
        queryset = self.get_queryset()
        serializer = UserSerializer(queryset, many=True)
        return Response(serializer.data)

GenericAPIView

GenericAPIView

This class extends REST framework’s APIView class, adding commonly required behavior for standard list and detail views. Each of the concrete generic views provided is built by combining GenericAPIView, with one or more mixin classes.

Attributes

Attributes Basic settings: The following attributes control the basic view behavior.

  • queryset - The queryset that should be used for returning objects from this view. Typically, you must either set this attribute, or override the get_queryset() method. If you are overriding a view method, it is important that you call get_queryset() instead of accessing this property directly, as queryset will get evaluated once, and those results will be cached for all subsequent requests.
  • serializer_class - The serializer class that should be used for validating and deserializing input, and for serializing output. Typically, you must either set this attribute, or override the get_serializer_class() method.
  • lookup_field - The model field that should be used to for performing object lookup of individual model instances. Defaults to ‘pk’. Note that when using hyperlinked APIs you’ll need to ensure that both the API views and the serializer classes set the lookup fields if you need to use a custom value.
  • lookup_url_kwarg - The URL keyword argument that should be used for object lookup. The URL conf should include a keyword argument corresponding to this value. If unset this defaults to using the same value as lookup_field.

queryset : 뷰로부터 객체를 반환한다. queryset 속성을 세팅하거나 get_queryset()를 오버라이드 하는 방법이 있다. 하지만 속성을 직접 접근해서 수정하기 보다는 get_queryset()을 호출해서 오버라이드 하는것이 좋다. 왜냐하면 그 이후에 이루어지는 요청을 캐싱할 수 있기때문이다.

serializer_class : 유효성검사, input값을 디 시리얼라이징(유저로부터 입력받은것을 django가 더 이해하기 편한 형태로 변환하는 과정), output을 시리얼라이징한다.

lookup_field : 모델 인스턴스에서 객체를 조회할 때 사용될 필드. 간단하게 말해서 PK(primary key). 기본값은 pk이다.

이 외에도 Pagination, Filtering 등이 있다.

Mixins

Mixins

The mixin classes provide the actions that are used to provide the basic view behavior. Note that the mixin classes provide action methods rather than defining the handler methods, such as .get() and .post(), directly. This allows for more flexible composition of behavior. The mixin classes can be imported from rest_framework.mixins. mixin classes는 기본 view의 기능을 제공한다. .get() .post()와 같은 handler methods를 직접 정의하기 보다는 action method를 제공하기 때문에 좀더 유연하게 응답을 구성할 수 있다.

Concrete View Classes

Concrete View Classes

The following classes are the concrete generic views. If you’re using generic views this is normally the level you’ll be working at unless you need heavily customized behavior. The view classes can be imported from rest_framework.generics.

동작을 커스텀할 경우가 아니라면 일반적으로 사용하게 될 클레스이다. 예를 들어

  1. CreateAPIView Used for create-only endpoints. Provides a post method handler. Extends: GenericAPIView , CreateModelMixin

  2. ListAPIView Used for read-only endpoints to represent a collection of model instances. Provides a get method handler. Extends: GenericAPIView , ListModelMixin

이외에도 다양하게 있다.

Customizing the generic views

Customizing the generic views Often you’ll want to use the existing generic views, but use some slightly customized behavior. If you find yourself reusing some bit of customized behavior in multiple places, you might want to refactor the behavior into a common class that you can then just apply to any view or viewset as needed.

Creating custom mixins

Creating custom mixins For example, if you need to lookup objects based on multiple fields in the URL conf, you could create a mixin class like the following:

class MultipleFieldLookupMixin(object):
    """
    Apply this mixin to any view or viewset to get multiple field filtering
    based on a `lookup_fields` attribute, instead of the default single field filtering.
    """
    def get_object(self):
        queryset = self.get_queryset()             # Get the base queryset
        queryset = self.filter_queryset(queryset)  # Apply any filter backends
        filter = {}
        for field in self.lookup_fields:
            if self.kwargs[field]: # Ignore empty fields.
                filter[field] = self.kwargs[field]
        obj = get_object_or_404(queryset, **filter)  # Lookup the object
        self.check_object_permissions(self.request, obj)
        return obj

Django Restframework Serializer(ForeignKey, nested objects)

|

django restframework serializer

Serializers - Django REST framework

Serializers allow complex data such as querysets and model instances to be converted to native Python datatypes that can then be easily rendered into JSON, XML or other content types. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.

시리얼라이저는 장고의 쿼리셋이나 모델의 인스턴스와 같은 복잡한 데이터를 python의 데이터 타입으로 변환하여 json, XML과 같은 타입으로 변환을 도와줍니다. 유효성을 검사한 뒤, 반대 작업인 deserialze와 다시 복잡한 데이터 형태로 파싱을 도와주기도 합니다.

serialize, save, validation, partial update와 같은 기본적인 기능은 넘어간다.

Dealing with nested objects

Dealing with nested objects
개인적으로 사이드 프로젝트를 하면서 이 부분에 대한 지식이 없어 이 키워드를 찾는데 오래걸렸다. Foreign Key관계에 있는 모델을 만들 때 유용하게 쓰이는 기능이다. 예를들어서 Song이라는 모델과 Archive라는 노래를 담는 보관함이 있다고 하자.

class Song(models.Model):
		title = PK
		singer = char
		created_at = date
		...

class Archive(models.Model):
		song = FK(Song)
		...

Django models.py에 모델을 이렇게 정의했고, 시리얼라이저도 같은 구조였다. 나의 프로젝트의 클라이언트(Vue.js)에서 Archive를 읽어왔을 때 Archive.song을 읽어오면 song object의 string값만 전달 받아 Song의 필드들(title, singer, created_at 등)의 값을 읽어올 수 없었다. 이 문제는 Archive의 song 필드에 song serializer를 대입해주면 해결된다.

아래는 내 경우와 다른경우이고, 공식문서에 있는 코드를 가져왔다.

class UserSerializer(serializers.Serializer):
    email = serializers.EmailField()
    username = serializers.CharField(max_length=100)

class CommentSerializer(serializers.Serializer):
    user = UserSerializer()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

The previous examples are fine for dealing with objects that only have simple datatypes, but sometimes we also need to be able to represent more complex objects, where some of the attributes of an object might not be simple datatypes such as strings, dates or integers.

Django Model Meta, attribute, methods (해시태그 기능을 위한 공부)

|

Django model - Meta, attribute, methods

1. Meta

Models | Django documentation | Django

from django.db import models

class Ox(models.Model):
    horn_length = models.IntegerField()

    class Meta:
        ordering = ["horn_length"]
        verbose_name_plural = "oxen"

Model metadata is “anything that’s not a field”, such as ordering options ( ordering ), database table name ( db_table ), or human-readable singular and plural names ( verbose_name and verbose_name_plural ). None are required, and adding class Meta to a model is completely optional. A complete list of all possible Meta options can be found in the model option reference .

모델 메타데이터는 필드가 아닌 모든것을 뜻합니다. 예를들어 정렬옵션, db table이름, 사람이 읽기 편한 이름 등이 있습니다. 필수는 아니고 옵션입니다. 관련 옵션들은 여기에 있습니다.

2. Model attributes (Manager)

Models | Django documentation | Django

objects

The most important attribute of a model is the Manager . It’s the interface through which database query operations are provided to Django models and is used to retrieve the instances from the database. If no custom Manager is defined, the default name is objects . Managers are only accessible via model classes, not the model instances.

모델의 가장 중요한 속성중 하나는 Manager 입니다. 이 인터페이스를 통해 Django models로 쿼리작업이 이루어지고 데이터베이스로부터 인스턴스를 받아올 수 있습니다. Manager를 따로 정해주지 않으면 디폴트 이름값은 objects입니다. models instances가 아닌 model class를 통해서만 접근할 수 있습니다.

Custom managers

Managers | Django documentation | Django

There are two reasons you might want to customize a Manager: to add extra Manager methods, and/or to modify the initial QuerySet the Manager returns.

커스텀하는 이유

  1. manager methods 추가
  2. initail queryset 수정

Adding extra manager methods

Managers | Django documentation | Django

Adding extra Manager methods is the preferred way to add “table-level” functionality to your models. (For “row-level” functionality – i.e., functions that act on a single instance of a model object – use Model methods , not custom Manager methods.) A custom Manager method can return anything you want. It doesn’t have to return a QuerySet. For example, this custom Manager offers a method with_counts(), which returns a list of all OpinionPoll objects, each with an extra num_responses attribute that is the result of an aggregate query:

from django.db import models

class PollManager(models.Manager):
    def with_counts(self):
        from django.db import connection
        with connection.cursor() as cursor:
            cursor.execute("""
                SELECT p.id, p.question, p.poll_date, COUNT(*)
                FROM polls_opinionpoll p, polls_response r
                WHERE p.id = r.poll_id
                GROUP BY p.id, p.question, p.poll_date
                ORDER BY p.poll_date DESC""")
            result_list = []
            for row in cursor.fetchall():
                p = self.model(id=row[0], question=row[1], poll_date=row[2])
                p.num_responses = row[3]
                result_list.append(p)
        return result_list

class OpinionPoll(models.Model):
    question = models.CharField(max_length=200)
    poll_date = models.DateField()
    objects = PollManager()

class Response(models.Model):
    poll = models.ForeignKey(OpinionPoll, on_delete=models.CASCADE)
    person_name = models.CharField(max_length=50)
    response = models.TextField()

3. Model methods

Models | Django documentation | Django

Define custom methods on a model to add custom “row-level” functionality to your objects. Whereas Manager methods are intended to do “table-wide” things, model methods should act on a particular model instance. This is a valuable technique for keeping business logic in one place – the model. For example, this model has a few custom methods:

모델의 객체에서 row level 수준으로 동작할 custom methods를 정의할 수 있다. Manager methods는 table wide인 반면 model methodssms 특정 모델 인스턴스에서 작동한다. 이는 비즈니스 로직에서 중요한 기술이다.

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    birth_date = models.DateField()

    def baby_boomer_status(self):
        "Returns the person's baby-boomer status."
        import datetime
        if self.birth_date < datetime.date(1945, 8, 1):
            return "Pre-boomer"
        elif self.birth_date < datetime.date(1965, 1, 1):
            return "Baby boomer"
        else:
            return "Post-boomer"

    @property
    def full_name(self):
        "Returns the person's full name."
        return '%s %s' % (self.first_name, self.last_name)

결론

요약

  1. model attribute (manager) : 하나의 모델, 하나의 테이블, 모델의 인스턴스에 국한된 속성
    Book.dahl_objects.all()
    Book.dahl_objects.filter(title='Matilda')
    Book.dahl_objects.count()
    
  2. model method : low level부터 접근가능, 다른 모델과 연계해서 사용할 수 있다.
    class Person(models.Model):
     first_name = models.CharField(max_length=50)
     last_name = models.CharField(max_length=50)
     birth_date = models.DateField()
    
     def baby_boomer_status(self):
         "Returns the person's baby-boomer status."
         import datetime
         if self.birth_date < datetime.date(1945, 8, 1):
             return "Pre-boomer"
         elif self.birth_date < datetime.date(1965, 1, 1):
             return "Baby boomer"
         else:
             return "Post-boomer"
    

내 프로젝트에 적용

내가 작업하고 있는 인스타그램 클론프로젝트의 Django : models.py의 일부분이다.

class Post(models.Model):
		...
    content = models.TextField(blank=True)
    tag_set = models.ManyToManyField("Tag")

class Tag(models.Model):
    name = models.CharField(max_length=100, unique=True)

내가 구현하려 했던 기능은

  1. Post를 작성하고 저장하면서 내용에서 해시태그를 추출
  2. 추출한 태그를 자동으로 Post table의 tag_set 필드에 저장.

model methods만 활용하면 충분하다고 판단했고 간략하게 다음과 같이 코딩할 예정이다.

class Post(models.Model):
		...
    content = models.TextField(blank=True)
    tag_set = models.ManyToManyField("Tag")

	  def set_tags(self):
		  tag_set = self.content에서 tag 추출
		  t = Tag.objects.get_or_create(name=tag_set)
		  self.tag_set.add(t)

#study/programming/django 9 Nov 2019 3:51 PM