ジェネリックリレーションのないジェネリック機能
36413 ワード
あなたが何かのようないくつかの一般的な機能を持っているときに、コメント、またはupvotable、それは使用するのが一般的ですGeneric Relations in Django . ジェネリックリレーションシップの問題は、データベースレベルではなくアプリケーションレベルでのリレーションシップを作成するためであり、ジェネリック機能を共有するコンテンツを集約する場合は、データベースクエリーが必要です.私がこの記事であなたに示す別の方法があります.
私は2002年に私の最初の仕事でこのテクニックを学び、次に数年前にジャンゴと再びそれを再発見しました.トリックは、ジェネリック
Item
他のすべての自律モデルが1対1の関係を持つモデルItem
. また、Item
モデルはitem_type
フィールドを使用すると、後方の1対1の関係を認識できます.次に、いくつかの一般的なカテゴリを持っている必要があるたびに、あなたは
Item.
メディアギャラリー、コメント、好き、またはupvotesのような一般的な機能を作成するたびにItem
. パーミッション、パブリッシングステータス、ワークフローを操作する必要があるときは、Item
. あなたがグローバル検索やゴミ箱を作成する必要があるたびに、あなたはItem
インスタンス.コードを見てみましょう.
アイテム
まず、作成します
items
つのモデルでアプリ:以前言及Item
と抽象モデルItemBase
様々なモデルが継承するために1対1の関係を持ちます.# items/models.py
import sys
from django.db import models
from django.apps import apps
if "makemigrations" in sys.argv:
from django.utils.translation import gettext_noop as _
else:
from django.utils.translation import gettext_lazy as _
class Item(models.Model):
"""
A generic model for all autonomous models to link to.
Currently these autonomous models are available:
- content.Post
- companies.Company
- accounts.User
"""
ITEM_TYPE_CHOICES = (
("content.Post", _("Post")),
("companies.Company", _("Company")),
("accounts.User", _("User")),
)
item_type = models.CharField(
max_length=200, choices=ITEM_TYPE_CHOICES, editable=False, db_index=True
)
class Meta:
verbose_name = _("Item")
verbose_name_plural = _("Items")
def __str__(self):
content_object_title = (
str(self.content_object) if self.content_object else "BROKEN REFERENCE"
)
return (
f"{content_object_title} ({self.get_item_type_display()})"
)
@property
def content_object(self):
app_label, model_name = self.item_type.split(".")
model = apps.get_model(app_label, model_name)
return model.objects.filter(item=self).first()
class ItemBase(models.Model):
"""
An abstract model for the autonomous models that will link to the Item.
"""
item = models.OneToOneField(
Item,
verbose_name=_("Item"),
editable=False,
blank=True,
null=True,
on_delete=models.CASCADE,
related_name="%(app_label)s_%(class)s",
)
class Meta:
abstract = True
def save(self, *args, **kwargs):
if not self.item:
model = type(self)
item = Item.objects.create(
item_type=f"{model._meta.app_label}.{model.__name__}"
)
self.item = item
super().save()
def delete(self, *args, **kwargs):
if self.item:
self.item.delete()
super().delete(*args, **kwargs)
それから、1と1対1の関係を持ついくつかの自律モデルを作成しましょうItem
. 「自律モデル」によって、私は自分自身で十分である、例えば、柱、会社または口座を意味します.型、カテゴリ、タグ、または好きなモデルは、自律的ではない.ポスト
第二に、私は
content
アプリケーションとPost
モデル.このモデルはItemBase
これは保存時に1対1の関係を作成し、item_type
AScontent.Post
:# content/models.py
import sys
from django.contrib.auth.base_user import BaseUserManager
from django.db import models
from django.contrib.auth.models import AbstractUser
if "makemigrations" in sys.argv:
from django.utils.translation import gettext_noop as _
else:
from django.utils.translation import gettext_lazy as _
from items.models import ItemBase
class Post(ItemBase):
title = models.CharField(_("Title"), max_length=255)
slug = models.SlugField(_("Slug"), max_length=255)
content = models.TextField(_("Content"))
class Meta:
verbose_name = _("Post")
verbose_name_plural = _("Posts")
会社
第三に、私は
companies
アプリケーションとCompany
モデル.このモデルもItemBase
これは保存時に1対1の関係を作成し、item_type
AScompanies.Company
:# companies/models.py
import sys
from django.contrib.auth.base_user import BaseUserManager
from django.db import models
from django.contrib.auth.models import AbstractUser
if "makemigrations" in sys.argv:
from django.utils.translation import gettext_noop as _
else:
from django.utils.translation import gettext_lazy as _
from items.models import ItemBase
class Company(ItemBase):
name = models.CharField(_("Name"), max_length=255)
slug = models.SlugField(_("Slug"), max_length=255)
description = models.TextField(_("Description"))
class Meta:
verbose_name = _("Company")
verbose_name_plural = _("Companies")
アカウント
第四に、私はより広範な例を持っている
accounts
アプリケーションを含むUser
モデル.このモデルはAbstractUser
からdjango.contrib.auth
だけでなくItemBase
一対一の関係のために.The item_type
設定するItem
モデルはaccounts.User
:# accounts/models.py
import sys
from django.db import models
from django.contrib.auth.base_user import BaseUserManager
from django.contrib.auth.models import AbstractUser
if "makemigrations" in sys.argv:
from django.utils.translation import gettext_noop as _
else:
from django.utils.translation import gettext_lazy as _
from items.models import ItemBase
class UserManager(BaseUserManager):
def create_user(self, username="", email="", password="", **extra_fields):
if not email:
raise ValueError("Enter an email address")
email = self.normalize_email(email)
user = self.model(username=username, email=email, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, username="", email="", password=""):
user = self.create_user(email=email, password=password, username=username)
user.is_superuser = True
user.is_staff = True
user.save(using=self._db)
return user
class User(AbstractUser, ItemBase):
# change username to non-editable non-required field
username = models.CharField(
_("Username"), max_length=150, editable=False, blank=True
)
# change email to unique and required field
email = models.EmailField(_("Email address"), unique=True)
bio = models.TextField(_("Bio"))
USERNAME_FIELD = "email"
REQUIRED_FIELDS = []
objects = UserManager()
新しいアイテムの作成
Djangoシェルを使用して、いくつかの自律モデルインスタンスと関連する項目を作成します.
>>> from content.models import Post
>>> from companies.models import Company
>>> from accounts.models import User
>>> from items.models import Item
>>> post = Post.objects.create(
... title="Hello, World!",
... slug="hello-world",
... content="Lorem ipsum…",
... )
>>> company = Company.objects.create(
... name="Aidas & Co",
... slug="aidas-co",
... description="Lorem ipsum…",
... )
>>> user = User.objects.create_user(
... username="aidas",
... email="[email protected]",
... password="jdf234oha&6sfhasdfh",
... )
>>> Item.objects.count()
3
すべてのそれらの関係から内容を集めること
最後に、1つのビューで投稿、企業、およびユーザーを持つ例です.そのためには、
Item
注釈付きのQuerySetfrom django import forms
from django.db import models
from django.shortcuts import render
from django.utils.translation import gettext, gettext_lazy as _
from .models import Item
class SearchForm(forms.Form):
q = forms.CharField(label=_("Search"), required=False)
def all_items(request):
qs = Item.objects.annotate(
title=models.Case(
models.When(
item_type="content.Post",
then="content_post__title",
),
models.When(
item_type="companies.Company",
then="companies_company__name",
),
models.When(
item_type="accounts.User",
then="accounts_user__email",
),
default=models.Value(gettext("<Untitled>")),
),
description=models.Case(
models.When(
item_type="content.Post",
then="content_post__content",
),
models.When(
item_type="companies.Company",
then="companies_company__description",
),
models.When(
item_type="accounts.User",
then="accounts_user__bio",
),
default=models.Value(""),
),
)
form = SearchForm(data=request.GET, prefix="search")
if form.is_valid():
query = form.cleaned_data["q"]
if query:
qs = qs.annotate(
search=SearchVector(
"title",
"description",
)
).filter(search=query)
context = {
"queryset": qs,
"search_form": form,
}
return render(request, "items/all_items.html", context)
最終語
あなたは、一般的な機能を持つことができますまだ
Item
包括的関係の代わりに1対1アプローチ名前
Item
モデルは異なることができ、あなたは、例えば、様々な目的のために複数のそのようなモデルを持つことができます.TaggedItem
タグのみ.あなたは、あなたのプロジェクトで類似した何でも使いますか?
どのようにこのアプローチを改善することができますか?
コメントで知らせてください!
カバー画像Pixabay
Reference
この問題について(ジェネリックリレーションのないジェネリック機能), 我々は、より多くの情報をここで見つけました https://dev.to/djangotricks/generic-functionality-without-generic-relations-1dfhテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol