#4 CRUD(4) - Assignment


課題として出てきた『飼い主と犬』『俳優と映画』について
仮想環境、プロジェクト、git repo生成、DB設定などの初期設定が完了し、飼い主と犬はfeature/owner行、俳優と映画はfeature/movie行に設定されている.

飼い主と子犬


owner/models.py

### My code 
from django.db import models

class Owner(models.Model):
    name  = models.CharField(max_length=45)
    email = models.CharField(max_length=300)
    age   = models.IntegerField() 
    
    class Meta:
        db_table = 'owners'

class Dog(models.Model):
    owner = models.ForeignKey('Owner',on_delete=models.CASCADE)
    name  = models.CharField(max_length=45)
    age   = models.IntegerField()
    
    class Meta:
        db_table = 'dogs'
### Wrap-up code
/...

    email = models.CharField(max_length=300,unique=True) 
    # email은 중복되면 안되므로 unique=True 작성
    age   = models.PositiveIntegerField() 
    # 나이는 음수가 되면 안되므로 PositiveIntegerField 사용

/...
それ以外に,課題単位でappを作成し,wrap−upではapp,飼い主,子犬のモデルをビュー単位で区分した.py urls.py views.一人一人がpyを作成し、必要に応じてインポートして使用する違いは明らかです.

owner/views.py


views.pyは以下のことを学びました.
  • 1)導入時に構築するモジュール/外部パッケージモジュール/カスタムモジュール規約
  • を遵守する.
  • 2)メソッド定義時に、目的/入出力コメントを使用して
  • を作成する.
  • 3)応答すると、owner idまたはcustomer idなどの要求元IDが生成される
  • 4)一対の多関係においてPOSTメソッドを作成する場合、外部キー参照はid(pk)、
  • である.
  • 5)一対の多関係においてGETメソッドを作成する際の逆参照の3つのメソッド
    (related_namegetまたはfilterの方法および_setの方法を使用する.
    dog.owner.テーブルネスト(nameなど)
  • 6)GETメソッドを実行すると、結果値がネストされたディックシェリー構造の場合、ネストされたリスト計算によって作成されます.
    4~6日まで詳しく知ります.まず4番のコードを比較してみましょう.
  • owner = Owner.objects.get(name=data["owner"])  # My code
    owner_id = data['owner_id'] 		       # Wrap-up code
    oneto multi関係では、multiに相当するテーブルがownerテーブルに関する情報をowner idとして持っているため、getクエリーownerテーブルを使用する必要がなく、post機能の作成時に問題がないと判断できます.
    次に、5番局参照と6番リスト計算が重なるのを見てみましょう.
    # OwnerView My code
    def get(self, request):
            owners = Owner.objects.all()
            result = []
            for owner in owners:
           	    dogs_list = [
                {'dog_name':dog.name} for dog in Dog.objects.filter(owner_id=owner.id)]
               	 ]
                result.append(
                  	 {
                      'name'  : owner.name,
                      'email' : owner.email,
                      'age'   : owner.age,
                      'dogs'  : dogs_list
                     }
               		)
            return JsonResponse({"dogs" : result}, status=200)
    
    # OwnerView Wrap-up code
            /...
            for owner in owners:
                results.append({
                    "id"   : owner.id,
                    "name" : owner.name,
                    "age"  : owner.age,
                    "dogs" : [{"id" : dog.id, "name" :dog.name} for dog in owner.dog_set.all()]
                }
                )
    飼い主が所有する子犬のリストdog listを作成し、私のコードでは簡単なダブルfor文を使用しましたが、wrap-upではリストコンパイルを使用しました.
    またmycodeでは、1つのテーブルで複数のテーブルを逆参照する場合、getまたはfilterメソッドを用い、wrap-upでは_setメソッドを用いた.
    wrap-upコードでは、ネストされたリストコンパイルを再度使用して、コードを再度簡潔に置き換えることができます.
    result = [{"id": owner.id, "name":owner.name,"age":owner.age,"dogs": [{"id" : dog.id, "name" : dog.name} for dog in owner.dog_set.all()]} for owner in owners]
    return JsonResponse({"MESSAGE" : result}, status=200)
    コードは簡潔になりましたが、長すぎて読みにくいので、途中で行を変えました.
    result = [{"id": owner.id, "name":owner.name,"age":owner.age,"dogs":
            [{"id" : dog.id, "name" : dog.name} for dog in owner.dog_set.all()]} for owner in owners]
    return JsonResponse({"MESSAGE" : result}, status=200)
    だいぶよくなった.
    このような適切なリスト計算の使用は計算性能を向上させるだけでなく、コードもより簡潔であり、適切な可読性があれば、できるだけ使用することが望ましい.
    # DogView My Code
    def get(self,request):
            dogs = Dog.objects.all()
            result = []
    # My code == case 3(many.one table)
            for dog in dogs:
                result.append({
                     'dog_name' : dog.name,
                     'dog_age' : dog.age,
                     'owner' : {
                         "id" : dog.owner.name,
                         "name" : dog.owner.id
                     }
                })
    # DogView Wrap-up code case1(related_name)
    	for dog in dogs:
                owner = Owner.objects.get(dogs__id = dog.id)
                result.append({
                     "id" : dog.id,
                     "name" : dog.name,
                     "age" : dog.age,
                     "owner" : {
                         "id" : owner.id,
                         "name" : owner.name
                     }
                 })
    # DogView Wrap-up code case2(NOT USE related_name)                 
            for dog in dogs:
                owner = Owner.objects.get(dog__name = dog.id)
                result.append({
                    "id" : dog.id,
                     "name" : dog.name,
                     "age" : dog.age,
                     "owner" : {
                         "id" : owner.id,
                         "name" : owner.name
                     }
                })             
    次にwrap-upで学んだ逆参照の3つの方法を順に見てみましょう.
    まず、case 1のようにrelated_nameを使用する場合は、まずモデルにrelated_nameを設定する必要があります.
    # models.py
    class Dog(models.Model):
        owner = models.ForeignKey('Owner',on_delete=models.CASCADE,related_name="dogs")
    related_nameとして指定されたdogsを使用する場合、ownerテーブルのデータではなくdogテーブルのデータを読み出す場合、owner.dogs.all()で閲覧できます.
    次に、case 2のようにrelated_nameを使用する必要はなく、dog_setのようにテーブル名setを使用して逆参照することもできる.この時がご主人様dog_set.all()で読み取ることができます.related_nameを使用しなくても逆参照できますが、使用していない場合は競合します.表名が複雑または長い場合は、related_nameを使用して短い置換を行うことができますので、できるだけ使用することをお勧めします.
    *dogs__idでは、関数のモデルクエリセット__は何を意味しますか?
    1つ目は現場ログインです.DjangoはSQLクエリ文で使用されるWHEREセクションも使用します.例:
    Owner.objects.filter(name__contains="김")
    上記のコードは、Ownerモデルのnameフィールドに「のり」を含むデータをフィルタリングするコードです.containsに加えて、gtstartwithなどの多くの現場位置決め方法が存在する.
    2つ目は외래키 모델의 속성 참조です.dog__idはこれに相当すると考えられる.
    views.pyの作成時にowner = Owner.objects.get(dog__id = dog.id)とともに作成し、ownerテーブルでdogテーブルを参照します.
    ここで、dogテーブルのpk idは、__によってownerによって参照されることができる.
    [__(二重下線)参照サイト]
    張高のモデル照会セットで、ダブルアンダースコア()はどういう意味ですか?
    ソース・サイト
    最後に、私が書いたコードと一致するcase 3を見てみましょう.
    上記の2つのケースとは異なり、owner = Owner.objects.get(dogs__id = dog.id)のコードは記述されていない.dog.owner.nameのようにテーブルの名前をネストしています.多くのdogモデルにはowner idという外部キーがあるため、getメソッドを使用することなくアクセスできます.

    俳優と映画

    git checkout -b命令によりfeature/movieブランチを生成して移動し、追加のタスクを行った.最初の設定からGET,POST方式の作成は最初の課題と似ているが,俳優と映画は一対多の関係ではなく,多対多の関係である.

    movie/models.py

    # models.py My code
    from django.db import models
    
    class Actor(models.Model):
        first_name     = models.CharField(max_length=45)
        last_name      = models.CharField(max_length=300)
        date_of_birth  = models.DateField()
        
        class Meta:
            db_table   = 'actors'
    
    class Movie(models.Model):
        title          = models.CharField(max_length=45)
        release_date   = models.DateField()
        running_time   = models.IntegerField()
        
        class Meta:
            db_table   = 'movies'
    
    class Actor_Movie(models.Model):
        actor          = models.ForeignKey("Actor",on_delete=models.CASCADE)
        movie          = models.ForeignKey("Movie",on_delete=models.CASCADE)
        
        class Meta:
            db_table   = 'actors_movies'
    
    # models.py Wrap-up code
        /..
        class ActorMovie(models.Model):
        
        /..
        class Actor(models.Model):
        /..
        	movies = models.ManyToManyField('movies.Movie', through = ActorMovie)
        
        /..
  • クラス名には、リファレンスバックグラウンドが使用されていません.コード規則を適用します.
  • djangoがサポートする多対多関係法ManyToManyField()
  • を用いる.
    当初、ManyToManyField()を使用すると、参照とコードの作成がより便利になりますが、中間テーブルはdjango内部で生成されるので、ManyToManyField()を使用しないで、中間テーブルのみを作成する必要があります.
    しかし、これはまったく間違った考えだ.djangoで内部に中間テーブルを作成するかどうかは、ManyToManyField()throughオプションがあるかどうかによって決まります.すなわち、throughオプションを使用しない場合、djangoは内部に中間テーブルを作成し、ユーザーが作成した中間テーブルを指定することができます.

    movie/views.py

    import json
    
    from django.views import View
    from django.http import JsonResponse
    
    from .models import Actor, Movie, Actor_Movie
    
    class ActorView(View):
        def get(self, request):
            actors = Actor.objects.all()
            result = []
            for actor in actors:
            
    # views.py My code (case 1, 중간테이블 사용)
    
                actors_movies = Actor_Movie.objects.filter(actor_id = actor.id)
                movie_list = []
                for actor_movie in actors_movies:
                    movie_list.append(
                        {
                            'title' : actor_movie.movie.title
                        }
                    )
                result.append(
                        {
                        'Name'        : actor_movie.actor.first_name + actor_movie.actor.last_name,
                        'Birth'       : actor_movie.actor.date_of_birth,
                        'Filmography' : movie_list
                        }    
                    )
                
            return JsonResponse({"Actors" : result}, status=200)
    
    	/..
    
    # views.py Wrap-up code (case2, Movie 테이블 이용)
    
    	/..	
    	movies = Movie.objects.filter(actor_movie__actor_id = actor.id)
    
                movie_list = []
                for movie in movies:
                    movie_list.append({
                        "id"    : movie.id,
                        "title" : movie.title
                    })
    
                results.append({
                    "first_name" : actor.first_name,
                    "last_name"  : actor.last_name,
                    "movies" : movie_list
    	/..
        
    # views. py Wrap_up code (case3, recommend)
    
    	/..
               results.append({
                    "first_name" : actor.first_name,
                    "last_name"  : actor.last_name,
                    "movies" : [{"title" : movie.title} for movie in actor.movies.all()]
                })
    	/..           
    
    djangoサポートを使用していないManyToManyField()、中間表のみ使用している私は、最初のGETメソッドを実現するために工夫を凝らしました.中間表だけを使って俳優のデータと俳優が出演した映画のデータをディックの繰り返し文に格納するのはコードが長いだけでなく、インデントも複雑だ.
    同様に、wrap-upからのcase 2も、中間テーブルではなくMovieテーブルを使用しますが、コードには大きな違いはありません.
    しかし,ManyToManyField()を用いたcase 3では,参照関係が容易になり,コード記述が容易になり,また中間テーブルを用いたためテーブル名が長くなり,コードが長くなる現象が消失することが見られる.throughでカスタムを中間テーブルとして指定すると、中間テーブル管理の問題もなくなります.
    符号化フレームワークにおいて予め定められた便利な方法の場合は,積極的に用いるべきである.
    「飼い主と犬」セクションでは、コードを簡潔に変更するように、オーバーラップリストcompressionを使用して適用します.

    最後に、中間テーブルを使用する場合、ActorViewと同じ構造のMovieViewをManyToManyField()を使用したリストコンパイル形式に変換する.

    モデルにはrelated_nameは使用されていないのでactor set.all()から逆参照が行われていることがわかります.