1. from functools import partial
    
  2. 
    
  3. from django.db import models
    
  4. from django.db.models.fields.related import (
    
  5.     RECURSIVE_RELATIONSHIP_CONSTANT,
    
  6.     ManyToManyDescriptor,
    
  7.     RelatedField,
    
  8.     create_many_to_many_intermediary_model,
    
  9. )
    
  10. 
    
  11. 
    
  12. class CustomManyToManyField(RelatedField):
    
  13.     """
    
  14.     Ticket #24104 - Need to have a custom ManyToManyField,
    
  15.     which is not an inheritor of ManyToManyField.
    
  16.     """
    
  17. 
    
  18.     many_to_many = True
    
  19. 
    
  20.     def __init__(
    
  21.         self,
    
  22.         to,
    
  23.         db_constraint=True,
    
  24.         swappable=True,
    
  25.         related_name=None,
    
  26.         related_query_name=None,
    
  27.         limit_choices_to=None,
    
  28.         symmetrical=None,
    
  29.         through=None,
    
  30.         through_fields=None,
    
  31.         db_table=None,
    
  32.         **kwargs,
    
  33.     ):
    
  34.         try:
    
  35.             to._meta
    
  36.         except AttributeError:
    
  37.             to = str(to)
    
  38.         kwargs["rel"] = models.ManyToManyRel(
    
  39.             self,
    
  40.             to,
    
  41.             related_name=related_name,
    
  42.             related_query_name=related_query_name,
    
  43.             limit_choices_to=limit_choices_to,
    
  44.             symmetrical=symmetrical
    
  45.             if symmetrical is not None
    
  46.             else (to == RECURSIVE_RELATIONSHIP_CONSTANT),
    
  47.             through=through,
    
  48.             through_fields=through_fields,
    
  49.             db_constraint=db_constraint,
    
  50.         )
    
  51.         self.swappable = swappable
    
  52.         self.db_table = db_table
    
  53.         if kwargs["rel"].through is not None and self.db_table is not None:
    
  54.             raise ValueError(
    
  55.                 "Cannot specify a db_table if an intermediary model is used."
    
  56.             )
    
  57.         super().__init__(
    
  58.             related_name=related_name,
    
  59.             related_query_name=related_query_name,
    
  60.             limit_choices_to=limit_choices_to,
    
  61.             **kwargs,
    
  62.         )
    
  63. 
    
  64.     def contribute_to_class(self, cls, name, **kwargs):
    
  65.         if self.remote_field.symmetrical and (
    
  66.             self.remote_field.model == "self"
    
  67.             or self.remote_field.model == cls._meta.object_name
    
  68.         ):
    
  69.             self.remote_field.related_name = "%s_rel_+" % name
    
  70.         super().contribute_to_class(cls, name, **kwargs)
    
  71.         if (
    
  72.             not self.remote_field.through
    
  73.             and not cls._meta.abstract
    
  74.             and not cls._meta.swapped
    
  75.         ):
    
  76.             self.remote_field.through = create_many_to_many_intermediary_model(
    
  77.                 self, cls
    
  78.             )
    
  79.         setattr(cls, self.name, ManyToManyDescriptor(self.remote_field))
    
  80.         self.m2m_db_table = partial(self._get_m2m_db_table, cls._meta)
    
  81. 
    
  82.     def get_internal_type(self):
    
  83.         return "ManyToManyField"
    
  84. 
    
  85.     # Copy those methods from ManyToManyField because they don't call super() internally
    
  86.     contribute_to_related_class = models.ManyToManyField.__dict__[
    
  87.         "contribute_to_related_class"
    
  88.     ]
    
  89.     _get_m2m_attr = models.ManyToManyField.__dict__["_get_m2m_attr"]
    
  90.     _get_m2m_reverse_attr = models.ManyToManyField.__dict__["_get_m2m_reverse_attr"]
    
  91.     _get_m2m_db_table = models.ManyToManyField.__dict__["_get_m2m_db_table"]
    
  92. 
    
  93. 
    
  94. class InheritedManyToManyField(models.ManyToManyField):
    
  95.     pass
    
  96. 
    
  97. 
    
  98. class MediumBlobField(models.BinaryField):
    
  99.     """
    
  100.     A MySQL BinaryField that uses a different blob size.
    
  101.     """
    
  102. 
    
  103.     def db_type(self, connection):
    
  104.         return "MEDIUMBLOB"