|
|
|
from django.db import models |
|
from django.utils.translation import gettext_lazy as _ |
|
from functools import partial |
|
from django.db.models.fields.related_descriptors import ManyToManyDescriptor |
|
|
|
class UnsignedForeignKey(models.ForeignKey): |
|
def db_type(self, connection): |
|
def self_bounded_get_internal_type(): |
|
return models.fields.PositiveIntegerField.get_internal_type(self.target_field) |
|
setattr(self.target_field, 'get_internal_type',self_bounded_get_internal_type) |
|
return models.fields.PositiveIntegerRelDbTypeMixin.rel_db_type(self.target_field, connection) |
|
|
|
def create_unsigned_many_to_many_intermediary_model(field, klass): |
|
def set_managed(model, related, through): |
|
through._meta.managed = model._meta.managed or related._meta.managed |
|
|
|
to_model = models.fields.related.resolve_relation(klass, field.remote_field.model) |
|
name = '%s_%s' % (klass._meta.object_name, field.name) |
|
models.fields.related.lazy_related_operation(set_managed, klass, to_model, name) |
|
|
|
to = models.fields.related.make_model_tuple(to_model)[1] |
|
from_ = klass._meta.model_name |
|
if to == from_: |
|
to = 'to_%s' % to |
|
from_ = 'from_%s' % from_ |
|
|
|
meta = type('Meta', (), { |
|
'db_table': field._get_m2m_db_table(klass._meta), |
|
'auto_created': klass, |
|
'app_label': klass._meta.app_label, |
|
'db_tablespace': klass._meta.db_tablespace, |
|
'unique_together': (from_, to), |
|
'verbose_name': _('%(from)s-%(to)s relationship') % {'from': from_, 'to': to}, |
|
'verbose_name_plural': _('%(from)s-%(to)s relationships') % {'from': from_, 'to': to}, |
|
'apps': field.model._meta.apps, |
|
}) |
|
|
|
return type(name, (models.Model,), { |
|
'Meta': meta, |
|
'__module__': klass.__module__, |
|
from_: models.ForeignKey( |
|
klass, |
|
related_name='%s+' % name, |
|
db_tablespace=field.db_tablespace, |
|
db_constraint=field.remote_field.db_constraint, |
|
on_delete=models.CASCADE, |
|
), |
|
to: UnsignedForeignKey( |
|
to_model, |
|
related_name='%s+' % name, |
|
db_tablespace=field.db_tablespace, |
|
db_constraint=field.remote_field.db_constraint, |
|
on_delete=models.CASCADE, |
|
) |
|
}) |
|
|
|
class UnsignedManyToMany(models.ManyToManyField): |
|
|
|
def contribute_to_class(self, cls, name, **kwargs): |
|
if self.remote_field.symmetrical and ( |
|
self.remote_field.model == "self" or self.remote_field.model == cls._meta.object_name): |
|
self.remote_field.related_name = "%s_rel_+" % name |
|
elif self.remote_field.is_hidden(): |
|
self.remote_field.related_name = "_%s_%s_+" % (cls.__name__.lower(), name) |
|
|
|
super().contribute_to_class(cls, name, **kwargs) |
|
|
|
if not cls._meta.abstract: |
|
if self.remote_field.through: |
|
def resolve_through_model(_, model, field): |
|
field.remote_field.through = model |
|
models.fields.related.lazy_related_operation(resolve_through_model, cls, self.remote_field.through, field=self) |
|
elif not cls._meta.swapped: |
|
self.remote_field.through = create_unsigned_many_to_many_intermediary_model(self, cls) |
|
|
|
setattr(cls, self.name, ManyToManyDescriptor(self.remote_field, reverse=False)) |
|
|
|
self.m2m_db_table = partial(self._get_m2m_db_table, cls._meta) |
|
|
|
##### Example: |
|
class TestTable(models.Model): |
|
class Meta: |
|
db_table = 'test_table' |
|
managed = False |
|
|
|
class RelatedTestModel(models.Model): |
|
unsigned_foreign_field = UnsignedManyToMany(TestTable) |
|
unsigned_m2m_field = UnsignedForeignKey(TestTable, on_delete=models.DO_NOTHING) |