在django中扩展站点模型的最佳方法是什么?创建一个新模型和ForeignKey网站还是有另一种方法可以让我继承Site模型?
我更喜欢子类化,因为从关系上来说我更舒服,但我担心它对内置管理员的影响。
答案 0 :(得分:8)
我刚使用自己的Site子类并为其创建了自定义管理员。
基本上,当您在django中对模型进行子类化时,它会创建指向父模型的FK,并允许透明地访问父模型的字段 - 就像您在pyhon中访问父类属性一样。 内置管理员不会受到任何影响,但您必须取消注册Sites ModelAdmin并注册您自己的ModelAdmin。
答案 1 :(得分:4)
如果您只想更改对象的行为,但不想添加任何新字段,则应考虑使用“代理模型”(Django 1.1中的新增功能)。您可以向现有模型添加额外的Python方法,以及更多:
这就是代理模型继承的用途:为原始模型创建代理。您可以创建,删除和更新代理模型的实例,并且将保存所有数据,就像使用原始(非代理)模型一样。不同之处在于您可以更改代理中的默认模型排序或默认管理器,而无需更改原始文件。
在the documentation中阅读更多内容。
答案 2 :(得分:2)
您可以使用其他模型,例如SiteProfile
与Site
具有OneToOne关系。
答案 3 :(得分:0)
从Django 2.2开始,仍然没有像Site
那样扩展User
的简单直接方法。现在最好的方法是创建新实体,并在其中放置参数。如果您想利用existing sites support,这是唯一的方法。
class SiteProfile(models.Model):
title = models.TextField()
site = models.OneToOneField(Site)
您将必须为SiteProfile
创建管理员。然后添加带有链接的SiteProfile
的一些Site
记录。现在,您可以在任何可以通过模型访问当前站点的地方使用site.siteprofile.title
。
答案 4 :(得分:0)
问这个问题已经有很长时间了,但我认为(Django 3.1)还没有像创建自定义用户模型那样的简单解决方案。在这种情况下,创建一个继承自django.contrib.auth.models.AbstractUser模型的自定义用户模型,并将AUTH_USER_MODEL(在设置中)更改为新创建的自定义用户模型即可解决此问题。
但是,对于Site模型,也可以通过以下详细解决方案来实现:
假设您有一个名称为core的应用程序。使用该应用程序执行以下所有代码(设置文件除外)。
# in core.models
...
from django.contrib.sites.models import Site
from django.db import models
class SiteProfile(models.Model):
"""SiteProfile model is OneToOne related to Site model."""
site = models.OneToOneField(
Site, on_delete=models.CASCADE, primary_key=True,
related_name='profiles', verbose_name='site')
long_name = models.CharField(
max_length=255, blank=True, null=True)
meta_name = models.CharField(
max_length=255, blank=True, null=True)
def __str__(self):
return self.site.name
class Meta:
app_label = 'sites' # make it under sites app (in admin)
...
如果您只想创建一个网站配置文件模型,那么到目前为止我们所做的工作已经足够了。但是,您将希望在迁移后立即创建第一个配置文件。因为创建了第一个站点,但没有创建与之相关的第一个配置文件。如果您不想手动创建它,则需要执行第三步。
# in core.apps
...
from django.conf import settings
from django.db.models.signals import post_migrate
def create_default_site_profile(sender, **kwargs):
"""after migrations"""
from django.contrib.sites.models import Site
from core.models import SiteProfile
site = Site.objects.get(id=getattr(settings, 'SITE_ID', 1))
if not SiteProfile.objects.exists():
SiteProfile.objects.create(site=site)
class CoreConfig(AppConfig):
name = 'core'
def ready(self):
post_migrate.connect(create_default_site_profile, sender=self)
from .signals import (create_site_profile) # now create the second signal
函数(create_default_site_profile)将使用post_migrate信号自动创建与迁移后的第一个站点相关的第一个配置文件。但是,您将需要另一个信号(post_save),即上述代码的最后一行。
# in core.signals
from django.contrib.sites.models import Site
from django.db.models.signals import post_save, post_migrate
from django.dispatch import receiver
from .models import SiteProfile
@receiver(post_save, sender=Site)
def create_site_profile(sender, instance, **kwargs):
"""This signal creates/updates a SiteProfile object
after creating/updating a Site object.
"""
siteprofile, created = SiteProfile.objects.update_or_create(
site=instance
)
if not created:
siteprofile.save()
您想在模板上使用它吗?例如
{{ site.name }}
然后您需要执行第5步和第6步。
'core.context_processors.site_processor'
# in settings.py
TEMPLATES = [
{
# ...
'OPTIONS': {
'context_processors': [
# ...
# custom processor for getting the current site
'core.context_processors.site_processor',
],
},
},
]
Site matching query does not exist.
,因此catch块如果为空,则会创建一个。如果您有第二个站点并将其删除,则此解决方案可能不完全合格。此解决方案仅创建一个ID = 1的网站。
# in core.context_processors
from django.conf import settings
from django.contrib.sites.models import Site
def site_processor(request):
try:
return {
'site': Site.objects.get_current()
}
except:
Site.objects.create(
id=getattr(settings, 'SITE_ID', 1),
domain='example.com', name='example.com')
您现在可以在模板中使用站点名称,域,元名称,长名称或添加的任何字段。
# e.g.
{{ site.name }}
{{ site.profiles.long_name }}
通常会添加两个数据库查询,一个用于File.objects,一个用于FileProfile.objects。但是,正如文档中所述,
Django is clever enough to cache the current site at the first request and it serves the cached data at the subsequent calls.
https://docs.djangoproject.com/en/3.1/ref/contrib/sites/#caching-the-current-site-object
答案 5 :(得分:0)
显然,您还可以在添加到 INSTALLED_APPS 的文件夹中创建一个 models.py 文件,内容如下:
from django.contrib.sites.models import Site as DjangoSite, SiteManager
from django.core.exceptions import ImproperlyConfigured
from django.db import models
from django.http.request import split_domain_port
# our site model
class Site(DjangoSite):
settings = models.JSONField(blank=True, default={})
port = models.PositiveIntegerField(null=True)
protocol = models.CharField(default='http', max_length=5)
@property
def url(self):
if self.port:
host = f'{self.domain}:{self.port}'
else:
host = self.domain
return f'{self.protocol}://{host}/'
# patch django.contrib.sites.models.Site.objects to use our Site class
DjangoSite.objects.model = Site
# optionnal: override get_current to auto create site instances
old_get_current = SiteManager.get_current
def get_current(self, request=None):
try:
return old_get_current(self, request)
except (ImproperlyConfigured, Site.DoesNotExist):
if not request:
return Site(domain='localhost', name='localhost')
host = request.get_host()
domain, port = split_domain_port(host)
Site.objects.create(
name=domain.capitalize(),
domain=host,
port=port,
protocol=request.META['wsgi.url_scheme'],
)
return old_get_current(self, request)
SiteManager.get_current = get_current