11.關於django的content_type表


  ******

Django的contenttype表中存放發的是app名稱和模型的對應關系

contentType使用方式
    - 導入模塊
        from django.contrib.contenttypes.models import ContentType
        from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
    - 定義models
        class PythonBasic(models.Model):
            course_name = models.CharField(max_length=32)
​
​
        class Oop(models.Model):
            course_name = models.CharField(max_length=32)
​
​
        class Coupon(models.Model):
            coupon_name = models.CharField(max_length=32)
            # 關聯到contenttype外鍵,用來存放對應的表的id(在contenttype表中的id)
            content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
            # 關聯對應的商品的id
            object_id = models.PositiveIntegerField()
            # 關聯的對象商品
            content_object = GenericForeignKey("content_type", "object_id")
    - 使用
        class CourseView(APIView):
            def get(self, request):
                pb = ContentType.objects.get(app_label="myapp", model="pythonbasic")
                # model_class()獲取對應的表類
                object_id = pb.model_class().objects.get(pk=3)
                # 其中的關系我們可以使用content_object進行關聯,它相當於一個管理器
                # 我們將對應的對象給他,他會自動給字段建立關系
                # 但是其實我對於它的應用了解還不是很多,如果需要請看django文檔
                Coupon.objects.create(coupon_name="python通關優惠券", content_object=object_id)
                return HttpResponse("ok")
   總結:
    關於它的使用時機,其實就是如果一個模型類與多個類之間都存在外鍵關系的時候。比如訂單表與書籍表以及衣服表,都會產生訂單,但是萬一還有其他類的商品也要關聯訂單的時候,我們可以使用,contentType進行關聯。其實就是一個萬能的中間表,可以關聯任何的其他表。
 
View Code

  當一張表作為多個表的FK,就可以使用content_type表;例如上面的優惠券表,被食物和衣服當作FK,數據庫表一旦建立就難以更改,如果以后需要增加電器等表並把優惠券表作為FK表,這時就不能做到在優惠券表增加列字段electr_id,因為表只能增加行記錄而不能增加列字段(雖然擴張表字段也可以,但是一個是浪費空間,第二個就是麻煩難道沒有一種商品類型你就加一個字段,累死了),因此就可以使用content_type表來將表與表中的對象進行關聯,從而做到不增加列字段的情況下增加FK關系。

  在使用content_type表時,需要在FK表中增加content_type作為FK字段,並增加GenericForeignKey便於優惠券表記錄的建立以及單個優惠券對象對應的其他商品的查找。在優惠券表關聯的“一”的關系表中增加GenericRelation字段便於查找關聯的優惠券記錄的查找

  

def get_order_serializer(queryset):
    class OrderItemSerializer(StandardModelSerializer):
        object_id = serializers.PrimaryKeyRelatedField(queryset=queryset)
        price = serializers.DecimalField(max_digits=10, decimal_places=2)

        class Meta:
            model = OrderItem
            fields = ("price", "object_id")

    class OrderCSerializer(StandardModelSerializer):
        item = OrderItemSerializer(many=True)

        class Meta:
            model = Order
            fields = ("item",)

    return OrderCSerializer, OrderItemSerializer


class OrderItemSerializer(StandardModelSerializer):
    name = serializers.CharField(source="content_type.name")

    class Meta:
        model = OrderItem
        fields = ('price', "name")


class OrderSerializer(StandardModelSerializer):
    item = OrderItemSerializer(many=True)   # 與嵌套的serializer中的model表中的related_name關聯

    class Meta:
        model = Order
        fields = ("amount", "item")
class OrderViewSet(base_view.BaseGenericViewSet,
                   CreateModelMixin,
                   ListModelMixin):
    queryset = Order.objects.all()
    serializer_class = OrderSerializer
    filter_backends = (StandardOrderingFilter, StandardFilterBackend, StandardSearchFilter)
    search_fields = ("amount", "id")
    filter_fields = ("amount", "id")
    ordering = "id"

    def create(self, request: Request, model, app_label):
        content_type = ContentType.objects.get(model=model, app_label=app_label)
        prd_model = content_type.model_class()
        order_ser, order_item_ser = get_order_serializer(prd_model._default_manager.all())
        serializer = order_ser(data=request.data)
        serializer.is_valid()
        data = serializer.validated_data
        order = Order.objects.create(amount=0)
        total_amount = 0
        for post_item in data["item"]:
            content_object = post_item["object_id"]
            price = content_object.get_price
            if price != post_item["price"]:
                raise Invalid
            total_amount += int(price)
            OrderItem.objects.create(content_object=content_object, price=price, order=order)
        order.amount = total_amount
        order.save()
        ser = self.get_serializer(order)
        headers = self.get_success_headers(ser.data)
        return Response(data=ser.data, headers=headers, status=status.HTTP_201_CREATED)
view.py
class Order(models.Model):
    amount = models.DecimalField(max_digits=10, decimal_places=2, default=-1)


class OrderItem(models.Model):
    content_type = models.ForeignKey(ContentType, on_delete=models.DO_NOTHING)
    object_id = models.IntegerField()
    content_object = GenericForeignKey()
    price = models.DecimalField(max_digits=10, decimal_places=2)
    order = models.ForeignKey(db_constraint=False, db_column="order_id",
                              db_index=True, on_delete=models.DO_NOTHING,
                              related_name="item", to="Order",
                              to_field="id")   # 設置的related_name是很關鍵的,因為在serializer傳入數據的時候,Orde表會根據它進行查找,如果名稱不對會發生錯誤


class Product(models.Model):
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=10, decimal_places=2)

    @property
    def get_price(self):
        return self.price

    class Meta:
        db_table = "product"
        verbose_name_plural = verbose_name = "product"

    def __str__(self):
        return "{}".format(self.name)
  1 contenttypes框架¶
  2 Django包含一個contenttypes應用程序,可以跟蹤Django驅動的項目中安裝的所有模型,為您的模型提供高級通用界面。
  3 
  4 概述¶
  5 contenttypes應用程序的核心是ContentType生活在的 模型 django.contrib.contenttypes.models.ContentType。ContentType表示和存儲有關項目中安裝的模型的信息的實例, 以及安裝ContentType新模型時自動創建的新實例 。
  6 
  7 ContentType具有返回它們所代表的模型類以及從這些模型查詢對象的方法的實例。ContentType 還有一個自定義管理器,它添加了使用ContentType和獲取ContentType 特定模型實例的方法。
  8 
  9 模型之間的關系 ContentType也可用於啟用某個模型的實例與已安裝的任何模型的實例之間的“通用”關系。
 10 
 11 安裝contenttypes框架¶
 12 contenttypes框架包含在由其INSTALLED_APPS創建的默認 列表中,但如果您已將其刪除,或者您手動設置了 列表,則可以通過添加到您的設置來啟用它 。django-admin startprojectINSTALLED_APPS'django.contrib.contenttypes'INSTALLED_APPS
 13 
 14 安裝contenttypes框架通常是個好主意; Django的其他幾個捆綁應用程序需要它:
 15 
 16 管理應用程序使用它來記錄通過管理界面添加或更改的每個對象的歷史記錄。
 17 Django 使用它將用戶權限綁定到特定模型。authentication framework
 18 該ContentType模型¶
 19 類ContentType¶
 20 每個實例ContentType 都有兩個字段,它們一起唯一地描述了已安裝的模型:
 21 
 22 app_label¶ (這個是你app的名稱 23 模型所屬應用程序的名稱。這取自app_label模型的屬性,僅包括應用程序的Python導入路徑的 最后部分; django.contrib.contenttypes,例如,成為一個 app_label的contenttypes。
 24 
 25 model¶ (這個是你模型的表名稱,全小寫)
 26 模型類的名稱。
 27 
 28 此外,還提供以下房產:
 29 
 30 name¶
 31 人類可讀的內容類型名稱。這取自verbose_name 模型的 屬性。
 32 
 33 讓我們看一個例子來看看它是如何工作的。如果您已安裝該contenttypes應用程序,然后添加 到您的 設置並運行以進行安裝,則該模型將安裝到您的數據庫中。除此之外,還將使用以下值創建一個新實例 :the sites applicationINSTALLED_APPSmanage.py migratedjango.contrib.sites.models.SiteContentType
 34 
 35 app_label 將被設置為'sites'(Python路徑的最后一部分django.contrib.sites)。
 36 model 將被設置為'site' 37 ContentType實例上的方法¶
 38 每個ContentType實例都有一些方法,允許您從ContentType實例獲取 它所代表的模型,或者從該模型中檢索對象:
 39 
  content_type = Content_type.objects.get(app_label="XXX", model="xxx") (下面用的ContConentType=content_type.model_class()就是這樣查找出來的)
  還有一個辦法可以拿到模型多有的對象 content_type.model_class._default_manager.all() (紅色部分其實就是objects模型管理器對象)
    
 40 ContConentType.get_object_for_this_type(** kwargs)¶ (uuid="ssfdsfa")其實就是傳入對應的字段與get相同  41 為 表示的模型獲取一組有效的查找參數ContentType,並 在該模型上執行,返回相應的對象。a get() lookup  42 
 43 ContentType.model_class()¶ # 可以拿到對應的模型 他很重要  44 返回此ContentType實例表示的模型類 。  45 
 46 例如,我們可以仰望 ContentType的 User模型:  47 
 48 >>> from django.contrib.contenttypes.models import ContentType  49 >>> user_type = ContentType.objects.get(app_label='auth', model='user')  50 >>> user_type  51 <ContentType: user>
 52 然后使用它來查詢特定的 User,或者訪問User模型類:  53 
 54 >>> user_type.model_class() 55 <class 'django.contrib.auth.models.User'>
 56 >>> user_type.get_object_for_this_type(username='Guido')  57 <User: Guido>
 58 一起, get_object_for_this_type() 並model_class()啟用兩個非常重要的用例:  59 
 60 使用這些方法,您可以編寫對任何已安裝模型執行查詢的高級通用代碼 - 您可以在運行時將一個app_label並 model傳入 ContentType查找,然后使用模型類或從中檢索對象。  61 您可以將另一個模型與 ContentType將其實例綁定到特定模型類的方法相關聯,並使用這些方法來訪問這些模型類。  62 Django的幾個捆綁應用程序使用后一種技術。例如, 在Django的身份驗證框架中使用帶有外鍵的 模型; 這可以 代表“可以添加博客條目”或“可以刪除新聞故事”等概念。the permissions systemPermissionContentTypePermission  63 
 64 該ContentTypeManager¶  65 類ContentTypeManager¶  66 ContentType還有一個自定義管理器,ContentTypeManager它添加了以下方法:  67 
 68 clear_cache()¶  69 清除用於ContentType跟蹤其已創建ContentType實例的模型 的內部緩存 。您可能不需要自己調用此方法; Django會在需要時自動調用它。  70 
 71 get_for_id(id)¶  72 ContentType按ID 查找。由於此方法使用相同的共享緩存 get_for_model(),因此優先使用此方法 ContentType.objects.get(pk=id)  73 
 74 get_for_model(model,for_concrete_model = True)¶ (傳入模型名)  75 獲取模型類或模型的實例,並返回ContentType表示該模型的 實例。for_concrete_model=False允許獲取ContentType代理模型。  76 
 77 get_for_models(* models,for_concrete_models = True)¶  78 獲取可變數量的模型類,並返回將模型類映射到ContentType表示它們的實例的字典 。for_concrete_models=False允許獲取 ContentType代理模型。  79 
 80 get_by_natural_key(app_label,model)¶  81 返回ContentType 由給定應用程序標簽和模型名稱唯一標識的實例。此方法的主要目的是允許 在反序列化期間ContentType通過自然鍵引用對象。  82 
 83 get_for_model()當您知道需要使用a ContentType但不想在獲取模型的元數據以執行手動查找時遇到麻煩,該方法特別有用 :  84 
 85 >>> from django.contrib.auth.models import User  86 >>> ContentType.objects.get_for_model(User)  87 <ContentType: user>
 88 通用關系¶  89 從您自己的模型中添加外鍵,以 ContentType允許模型有效地將自身綁定到另一個模型類,如Permission上面模型的示例 所示。但是可以更進一步,用於 ContentType在模型之間實現真正通用(有時稱為“多態”)關系。  90 
 91 一個簡單的例子是標記系統,它可能如下所示:  92 
 93 from django.contrib.contenttypes.fields import GenericForeignKey  94 from django.contrib.contenttypes.models import ContentType  95 from django.db import models  96 
 97 class TaggedItem(models.Model):  98     tag = models.SlugField()  99     content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) 100     object_id = models.PositiveIntegerField() 101     content_object = GenericForeignKey('content_type', 'object_id') 102 
103     def __str__(self): 104         return self.tag 105 法線ForeignKey只能“指向”另一個模型,這意味着如果TaggedItem模型使用了 ForeignKey它,則必須選擇一個且只有一個模型來存儲標簽。contenttypes應用程序提供了一個特殊的字段類型(GenericForeignKey),它可以解決這個問題,並允許關系與任何模型: 106 
107 類GenericForeignKey¶ 108 設置一個有三個部分 GenericForeignKey: 109 
110 給你的模型ForeignKey 來ContentType。該字段的通常名稱是“content_type”。 111 為您的模型提供一個字段,該字段可以存儲您要與之相關的模型中的主鍵值。對於大多數模型,這意味着一個 PositiveIntegerField。該字段的通常名稱是“object_id”。 112 給你的模型a GenericForeignKey,並傳遞上述兩個字段的名稱。如果這些字段名為“content_type”和“object_id”,則可以省略 - 這些是GenericForeignKey將要查找的默認字段名稱 。 113 for_concrete_model¶ 114 如果False,該字段將能夠引用代理模型。默認是True。這反映了這個for_concrete_model論點 get_for_model()。 115 
116 主鍵類型兼容性 117 
118 “object_id”字段不必與相關模型上的主鍵字段具有相同的類型,但它們的主鍵值必須通過其get_db_prep_value()方法可強制化為與“object_id”字段相同的類型 。 119 
120 例如,如果要允許與具有任一IntegerField或 CharField主鍵字段的模型的泛型關系 ,則可以使用CharField模型上的“object_id”字段,因為整數可以通過強制轉換為字符串get_db_prep_value()。 121 
122 為了獲得最大的靈活性,您可以使用 TextField沒有定義最大長度的a,但是這可能會導致嚴重的性能損失,具體取決於您的數據庫后端。 123 
124 對於哪種字段類型最好,沒有一種通用的解決方案。您應該評估您希望指向的模型,並確定哪種解決方案對您的用例最有效。 125 
126 序列化ContentType對象的引用 127 
128 如果fixtures從實現泛型關系的模型中序列化數據(例如,生成時 ),則應該使用自然鍵來唯一標識相關ContentType 對象。請參閱自然鍵和 更多信息。dumpdata --natural-foreign 129 
130 這將啟用類似於用於正常的API的API ForeignKey; 每個TaggedItem都有一個content_object返回與其相關的對象的字段,您也可以在創建時使用該字段或使用它TaggedItem: 131 
132 >>> from django.contrib.auth.models import User 133 >>> guido = User.objects.get(username='Guido') 134 >>> t = TaggedItem(content_object=guido, tag='bdfl') 135 >>> t.save() 136 >>> t.content_object 137 <User: Guido>
138 如果刪除了相關對象,則content_type和object_id字段仍將設置為其原始值並GenericForeignKey返回 None: 139 
140 >>> guido.delete() 141 >>> t.content_object  # returns None
142 由於方式GenericForeignKey 實現,你不能直接使用的過濾器等領域(filter() 以及exclude()通過數據庫API,例如)。由於 GenericForeignKey不正常的領域對象,這些例子不工作: 143 
144 # This will fail
145 >>> TaggedItem.objects.filter(content_object=guido) 146 # This will also fail
147 >>> TaggedItem.objects.get(content_object=guido) 148 同樣,GenericForeignKeys沒有出現在ModelForms中。 149 
150 反向泛型關系¶ 151 類GenericRelation¶ 152 related_query_name¶ 153 默認情況下,相關對象與該對象的關系不存在。設置related_query_name創建從相關對象回到此關系的關系。這允許從相關對象查詢和過濾。 154 
155 如果您知道最常使用的模型,還可以添加“反向”通用關系以啟用其他API。例如: 156 
157 from django.contrib.contenttypes.fields import GenericRelation 158 from django.db import models 159 
160 class Bookmark(models.Model): 161     url = models.URLField() 162     tags = GenericRelation(TaggedItem) 163 Bookmark每個實例都有一個tags屬性,可以用來檢索它們的關聯TaggedItems: 164 
165 >>> b = Bookmark(url='https://www.djangoproject.com/') 166 >>> b.save() 167 >>> t1 = TaggedItem(content_object=b, tag='django') 168 >>> t1.save() 169 >>> t2 = TaggedItem(content_object=b, tag='python') 170 >>> t2.save() 171 >>> b.tags.all() 172 <QuerySet [<TaggedItem: django>, <TaggedItem: python>]>
173 GenericRelation使用 related_query_nameset 定義允許從相關對象查詢: 174 
175 tags = GenericRelation(TaggedItem, related_query_name='bookmark') 176 這使得過濾,排序等查詢操作上的Bookmark 來自TaggedItem: 177 
178 >>> # Get all tags belonging to bookmarks containing `django` in the url
179 >>> TaggedItem.objects.filter(bookmark__url__contains='django') 180 <QuerySet [<TaggedItem: django>, <TaggedItem: python>]>
181 當然,如果您不添加related_query_name,您可以手動執行相同類型的查找: 182 
183 >>> bookmarks = Bookmark.objects.filter(url__contains='django') 184 >>> bookmark_type = ContentType.objects.get_for_model(Bookmark) 185 >>> TaggedItem.objects.filter(content_type__pk=bookmark_type.id, object_id__in=bookmarks) 186 <QuerySet [<TaggedItem: django>, <TaggedItem: python>]>
187 就像GenericForeignKey 接受content-type和object-ID字段的名稱一樣,也是如此 GenericRelation; 如果具有通用外鍵的模型對這些字段使用非默認名稱,則必須在設置字段時傳遞字段的名稱 GenericRelation。例如,如果TaggedItem上面提到的模型使用了命名的字段content_type_fk並 object_primary_key創建了它的通用外鍵,那么 GenericRelation需要像這樣定義返回它: 188 
189 tags = GenericRelation( 190  TaggedItem, 191     content_type_field='content_type_fk', 192     object_id_field='object_primary_key', 193 ) 194 另請注意,如果刪除具有a的對象,則 GenericRelation任何GenericForeignKey 指向該對象的對象也將被刪除。在上面的示例中,這意味着如果Bookmark刪除了某個對象,TaggedItem則會同時刪除指向該對象的任何對象。 195 
196 不同ForeignKey, GenericForeignKey不接受on_delete自定義此行為的參數; 如果需要,您可以通過不使用來避免級聯刪除 GenericRelation,並且可以通過pre_delete 信號提供替代行為。 197 
198 通用關系和聚合¶ 199 Django的數據庫聚合API適用於 GenericRelation。例如,您可以找出所有書簽的標簽數量: 200 
201 >>> Bookmark.objects.aggregate(Count('tags')) 202 {'tags__count': 3} 203 表格中的通用關系¶ 204 該django.contrib.contenttypes.forms模塊提供: 205 
206 BaseGenericInlineFormSet 207 一個formset工廠,generic_inlineformset_factory()用於 GenericForeignKey。 208 類BaseGenericInlineFormSet¶ 209 generic_inlineformset_factory(model,form = ModelForm,formset = BaseGenericInlineFormSet,ct_field =“content_type”,fk_field =“object_id”,fields = None,exclude = None,extra = 3,can_order = False,can_delete = True,max_num = None,formfield_callback = None,validate_max = False,for_concrete_model = True,min_num = None,validate_min = False)¶ 210 返回一個GenericInlineFormSet使用 modelformset_factory()。 211 
212 您必須提供ct_field與fk_field他們是否是不同的默認值,content_type並object_id分別。其它參數類似於那些記錄 modelformset_factory()和 inlineformset_factory()。 213 
214 該for_concrete_model參數對應 for_concrete_model 的參數GenericForeignKey。 215 
216 管理中的通用關系¶ 217 該django.contrib.contenttypes.admin模塊提供 GenericTabularInline和 GenericStackedInline(子類 GenericInlineModelAdmin) 218 
219 這些類和函數支持在表單和管理員中使用通用關系。有關更多信息,請參閱模型formset和 管理文檔。 220 
221 類GenericInlineModelAdmin¶ 222 在GenericInlineModelAdmin 類繼承自的所有屬性 InlineModelAdmin類。但是,它增加了一些用於處理泛型關​​系的東西: 223 
224 ct_field¶ 225 ContentType模型上的外鍵字段的名稱 。默認為content_type。 226 
227 ct_fk_field¶ 228 表示相關對象ID的整數字段的名稱。默認為object_id。 229 
230 類GenericTabularInline¶ 231 類GenericStackedInline¶ 232 GenericInlineModelAdmin分別具有堆疊和表格布局的子類。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM