Stack Overflow에서 Django의 Content Types에 대한 해답
# ourapp.models
from django.conf import settings
from django.db import models
# Assign the User model in case it has been "swapped"
User = settings.AUTH_USER_MODEL
# Create your models here
class Post(models.Model):
author = models.ForeignKey(User)
title = models.CharField(max_length=75)
slug = models.SlugField(unique=True)
body = models.TextField(blank=True)
class Picture(models.Model):
author = models.ForeignKey(User)
image = models.ImageField()
caption = models.TextField(blank=True)
class Comment(models.Model):
author = models.ForeignKey(User)
body = models.TextField(blank=True)
post = models.ForeignKey(Post)
picture = models.ForeignKey(Picture)
Okay, so we do have a way to theoretically create this relationship. However, as a Python programmer, your superior intellect is telling you this sucks and you can do better. High five!
Enter the Content Types framework!
Well, now we're going to take a close look at our models and rework them to be more "reusable"and intuitive. Let's start by getting rid of the two foreign keys on our
Comment
model and replace them with a GenericForeignKey
. # ourapp.models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
...
class Comment(models.Model):
author = models.ForeignKey(User)
body = models.TextField(blank=True)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey()
So, what happened? Well, we went in and added the necessary code to allow for a generic relation to other models. Notice how there is more than just a
GenericForeignKey
, but also a ForeignKey
to ContentType
and a PositiveIntegerField
for the object_id
. These fields are for telling Django what type of object this is related to and what the id is for that object. In reality, this makes sense because Django will need both to lookup these related objects. Well, that's not very Python-like... its kinda ugly!
You are probably looking for air-tight, spotless, intuitive code that would make Guido van Rossumproud. I get you. Let's look at the
GenericRelation
field so we can put a pretty bow on this. # ourapp.models
from django.contrib.contenttypes.fields import GenericRelation
...
class Post(models.Model):
author = models.ForeignKey(User)
title = models.CharField(max_length=75)
slug = models.SlugField(unique=True)
body = models.TextField(blank=True)
comments = GenericRelation('Comment')
class Picture(models.Model):
author = models.ForeignKey(User)
image = models.ImageField()
caption = models.TextField(blank=True)
comments = GenericRelation('Comment')
Bam! Just like that you can work with the Comments for these two models. In fact, let's go ahead and do that in our shell (type
python manage.py shell
from your Django project directory). >>> from django.contrib.auth import get_user_model
>>> from ourapp.models import Picture
# We use get_user_model() since we are referencing directly
User = get_user_model()
# Grab our own User object
>>> me = User.objects.get(username='myusername')
# Grab the first of our own pictures so we can comment on it
>>> pic = Picture.objects.get(author=me)
# Let's start making a comment for our own picture
>>> pic.comments.create(author=me, body="Man, I'm cool!")
# Let's go ahead and retrieve the comments for this picture now
>>> pic.comments.all()
[]
It's that simple.
What are the other practical implications of these "generic" relations?
Generic foreign keys allow for less intrusive relations between various applications. For example, let's say we pulled the Comment model out into it's own app named chatterly
. Now we want to create another application named noise_nimbus
where people store their music to share with others. What if we want to add comments to those songs? Well, we can just draw a generic relation:
# noise_nimbus.models
from django.conf import settings
from django.contrib.contenttypes.fields import GenericRelation
from django.db import models
from chatterly.models import Comment
# For a third time, we take the time to ensure custom Auth isn't overlooked
User = settings.AUTH_USER_MODEL
# Create your models here
class Song(models.Model):
'''
A song which can be commented on.
'''
file = models.FileField()
author = models.ForeignKey(User)
title = models.CharField(max_length=75)
slug = models.SlugField(unique=True)
description = models.TextField(blank=True)
comments = GenericRelation(Comment)
hope you guys found this helpful as I would have loved to have come across something that showed me the more realistic application of GenericForeignKey
and GenericRelation
fields.
Is this too good to be true?
As with anything in life, there are pros and cons. Anytime you add more code and more abstraction, the underlying processes becomes heavier and a bit slower. Adding generic relations can add a little bit of a performance dampener despite the fact it will try and smart cache its results. All in all, it comes down to whether the cleanliness and simplicity outweighs the small performance gains. For me, the answer is a million times yes.
There is more to the Content Types framework than I have displayed here. There is a whole level of granularity and more verbose usage, but for the average individual, this is how you will be using it 9 out of 10 times in my opinion.
Generic relationizers(?) beware!
A rather large caveat is that when you use a GenericRelation
, if the model which has the GenericRelation
applied ( Picture
) is deleted, all related ( Comment
) objects will also be deleted. Or at least as of the time of this writing.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Django 라우팅 계층 URLconf 작용 및 원리 해석URL 구성(URLconf)은 Django가 지원하는 웹 사이트의 디렉토리와 같습니다.그것의 본질은 URL과 이 URL을 호출할 보기 함수 사이의 맵표입니다. 위의 예제에서는 URL의 값을 캡처하고 위치 매개 변수로...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.