[英]How to model this in django (inherited model, where each inherited model has a unique method)
[英]Altering unique fields of inherited Django model
在 Django 中,我正在尝试创建一个基本模型,该模型可用于以透明的方式跟踪其他模型数据的不同版本。 就像是:
class Warehouse(models.Model):
version_number = models.IntegerField()
objects = CustomModelManagerFilteringOnVersionNumber()
class Meta:
abstract=True
def save(self, *args, **kwargs):
# handling here version number incrementation
# never overwrite data but create separate records,
# don't delete but mark as deleted, etc.
pass
class SomeData(Warehouse):
id = models.IntegerField( primary_key=True )
name = models.CharField(unique=True)
我的问题是SomeData.name
实际上不是唯一的,元组('version_number', 'name' )
是。
我知道我可以将SomeData
的Meta
类与SomeData
一起unique_together
但我想知道这是否可以以更透明的方式完成。 也就是说,动态修改/创建这个unique_together
字段。
最后一点:也许用模型继承来处理这个问题不是正确的方法,但如果我能处理这个字段唯一性问题,它看起来对我很有吸引力。
编辑:显然,在这种情况下,“动态”一词具有误导性。 我确实希望在数据库级别保留唯一性。 这个想法是拥有不同版本的数据。
在这里给出一个上面模型的小例子(假设一个抽象模型继承,所以一切都在同一个表中):
数据库可能包含:
version_number | id | name | #comment 0 | 1 | bob | first insert 0 | 2 | nicky | first insert 1 | 1 | bobby | bob changed is name on day x.y.z 0 | 3 | malcom| first insert 1 | 3 | malco | name change 2 | 3 | momo | again...
并且我的自定义模型管理器将过滤数据(获取每个唯一 ID 的最大版本号),以便: SomeData.objects.all() 将返回
id | name 1 | bobby 2 | nicky 3 | momo
并且还将提供其他方法可以返回版本 n-1 中的数据。
显然,我将使用时间戳而不是版本号,以便我可以在给定日期检索数据,但原理保持不变。
现在的问题是,当我对上面的模型执行./manage.py makemigrations时,当我需要的是('SomeData.id','version_number') 上的唯一性时,它会在SomeData.name和SomeData.id上强制执行唯一性和('SomeData.name', 'version_number') 。 明确地,我需要将基本模型中的一些字段附加到在 db 中声明为唯一的继承模型的字段中,并且在运行manage.py命令和/或生产服务器运行时都可以这样做。
通过“动态修改/创建这个unique_together
字段”,我假设您的意思是您想在代码级别而不是数据库级别强制执行此操作。
您可以在模型的save()
方法中执行此操作:
from django.core.exceptions import ValidationError
def save(self, *args, **kwargs):
if SomeData.objects.filter(name=self.name, version_number=self.version_number).exclude(pk=self.pk).exists():
raise ValidationError('Such thing exists')
return super(SomeData, self).save(*args, **kwargs)
希望能帮助到你!
好的,我结束了从 django.db.models.base 对 ModelBase 进行子类化,以便我可以将声明为唯一的任何字段插入到“unique_together”中。 这是我当前的代码。 它尚未实现管理器和保存方法,但正确处理了数据库唯一性约束。
from django.db.models.options import normalize_together
from django.db.models.base import ModelBase
from django.db.models.fields import Field
class WarehouseManager( models.Manager ):
def get_queryset( self ):
"""Default queryset is filtered to reflect the current status of the db."""
qs = super( WarehouseManager, self ).\
get_queryset().\
filter( wh_version = 0 )
class WarehouseModel( models.Model ):
class Meta:
abstract = True
class __metaclass__(ModelBase):
def __new__(cls, name, bases, attrs):
super_new = ModelBase.__new__
meta = attrs.get( 'Meta', None )
try:
if attrs['Meta'].abstract == True:
return super_new(cls, name, bases, attrs )
except:
pass
if meta is not None:
ut = getattr( meta, 'unique_together', () )
ut = normalize_together( ut )
attrs['Meta'].unique_together = tuple( k+('wh_version',) for k in ut )
unique_togethers = ()
for fname,f in attrs.items():
if fname.startswith( 'wh_' ) or not isinstance( f, Field ):
continue
if f.primary_key:
if not isinstance( f, models.AutoField ):
raise AttributeError( "Warehouse inherited models cannot "
"define a primary_key=True field which is not an "
"django.db.models.AutoField. Use unique=True instead." )
continue
if f.unique:
f._unique = False
unique_togethers += ( (fname,'wh_version'), )
if unique_togethers:
if 'Meta' in attrs:
attrs['Meta'].unique_together += unique_togethers
else:
class DummyMeta: pass
attrs['Meta'] = DummyMeta
attrs['Meta'].unique_together = unique_togethers
return super_new(cls, name, bases, attrs )
wh_date = models.DateTimeField(
editable=False,
auto_now=True,
db_index=True
)
wh_version = models.IntegerField(
editable=False,
default=0,
db_index=True,
)
def save( self, *args, **kwargs ):
# handles here special save behavior...
return super( WarehouseModel, self ).save( *args, **kwargs )
objects = WarehouseManager()
class SomeData( WarehouseModel ):
pid = models.IntegerField( unique=True )
class SomeOtherData( WarehouseModel ):
class Meta:
unique_together = ('pid','chars')
pid = models.IntegerField()
chars = models.CharField()
只是为了添加到__metaclass__
的答案中,您可能需要使用__metaclass__
类,如下所示
class ModelBaseMeta(BaseModel):
def __new__(cls, name, bases, attrs):
...
...
class YourAbstractClass(models.Model, metaclass=ModelBaseMeta):
class Meta:
abstract = True
而不是将__metaclass__
定义直接放在抽象类中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.