How to add model field dynamically? - django

I want to specify a model field dynamically in models.py. For example:
class ModelA(models.Model):
pass # whatever
# immediately after class declaration in models.py
if django_is_not_non_rel:
MyModel.all_modelb = models.ManyToManyField(ModelB, through="ModelAB")
But this code example does not work. When the condition is true, ModelA does not have the all_modelb field after everything is initialized. Why???? How do I effect my intention?

Creating columns dynamically is not impossible with Django but you'll have to do it using a metaclass and/or inheritance.
For your problem the easiest solution would probably be something like this:
if django_is_not_non_rel:
class ModelABase(models.Model):
MyModel.all_modelb = models.ManyToManyField(ModelB, through="ModelAB")
class Meta:
abstract = True
else:
class ModelABase(models.Model):
class Meta:
abstract = True
class ModelA(ModelABase):
pass # whatever
Or even simpler (if you don't need as much customization):
class ModelAORMBase(models.Model):
MyModel.all_modelb = models.ManyToManyField(ModelB, through="ModelAB")
class Meta:
abstract = True
if django_is_not_non_rel:
ModelABase = ModelAORMBase
else:
ModelABase = models.Model
class ModelA(ModelABase):
pass # whatever

Related

How to assign the attribute of SubFactory instead of the SubFactory itself

I need the attribute of the SubFactory instead of the object created by it.
# models.py
class User:
pass
class UserProfile:
user = models.OneToOneField(User)
class Job:
user = models.ForeignKey(User)
# factories.py
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = User
class UserProfileFactory(factory.django.DjangoModelFactory):
class Meta:
model = UserProfile
user = factory.SubFactory(UserFactory)
class JobFactory(factory.django.DjangoModelFactory):
class Meta:
model = Job
# for certain reasons, I want to use UserProfileFactory here but get the user generated from it
user = factory.SubFactory(UserProfileFactory).user # doesn't work but you get the idea
The best way is to combine class Params and factory.SelfAttribute:
class JobFactory(factory.django.DjangoModelFactory):
class Meta:
model = Job
class Params:
profile = factory.SubFactory(ProfileFactory)
user = factory.SelfAttribute("profile.user")
A parameter is used inside the factory, but discarded before calling the model.
This way:
You can provide a profile to the factory if you have it around: JobFactory(profile=foo)
You can set some sub-field of the profile' user: JobFactory(profile__user__username="john.doe")
I went with the approach below which might be useful to some:
user = factory.LazyFunction(lambda: UserProfileFactory().user)
That said, it's not documented if this is a legitimate way of using factories, so feel free to correct if this is wrong.

Django Rest Framework: Shared Fields on Serializers

I have a few Serializers that share a few fields like meta_id, category_id and so on.
Obviously I could just declare these on the serializer as a SerializerMethodField individually but I would like to find a way to reuse the logic, either with a Mixin, Decorator, or inheritance.
How can I declare a base serializer and inherit it— while still inheriting from serializers.ModelSerializer? So that I can reuse the get_meta_id and make sure it shows up in the fields?
class Foo(serializers.ModelSerializer, somethingHere?):
meta_id = Serializers.SerializerMethodField()
class Meta:
model = Foo
fields = [...]
def get_meta_id(self, obj):
...
Is it possible to just pass two parameters into the class
You can crete a Base serializer and use inheritence for other serializers.Like that:
class BaseSerializer(serializers.Serializer):
#your codes and extra fields
test_field = serializer.SerializerMethodField()
def get_test_field(self.obj):
return 'test' # or you can use obj instance here
class ExtendedSerializer(BaseSerializer,serializers.ModelSerializer):
#your extra fields
class Meta:
fields = BaseSerializer.Meta.fields + () # you can add your additional fields here
class BaseSerializer(serializers.ModelSerializer): # The BaseSerializer class inherit everthing from ModelSerializer
class Meta(serializers.ModelSerializer.Meta): # The BaseSerializer.Meta inherits everything from ModelSerializer.Meta
def get_meta_id(self, obj):
return self.meta_id
class Foo(BaseSerializer):
meta_id = Serializers.SerializerMethodField()
class Meta(BaseSerializer.Meta): # Here you should get the ModelSerializer.Meta + your custom get_meta_id
model = Foo
fields = [...]

Django Add methods to all models

I'm creating multiple Django apps. I want to add to every model a function, exist. Can I add this function to the built-in model or do I have to use a proxy model?
if you use proxy you will need to create an class proxy per model in your app. this is no good idea.
how to use proxy
you should use Abstract class.
BaseClass(models.Model):
#here fields option
def do_something(self):
print('Im cool')
class Meta:
abstract = True
SimpleClass(BaseClass):
# here yours fields
class Meta:
abstract = False
AnotherClass(BaseClass):
# here yours fields
class Meta:
abstract = False
simple = SimpleClass()
simple.do_something() #it will print out 'im cool'
another = AnotherClass()
another.do_something() #it will print out 'im cool'
if you want more information about abstract class

How to implement two abstract models with ForeignKey relation of one to other in Django?

I.e. we have SomeSeries with several SomeDecors, where ForeignKey of SomeDecor points to SomeSeries. I want both to be abstract and later instantiate several pairs of it (with it's own tables in db). Is it possible?
I.e.
class SomeSeries(models.Model):
class Meta:
abstract = True
vendor = models.ForeignKey(Vendor)
name = models.CharField(max_length=255, default='')
def __unicode__(self):
return "{} {}".format(self.vendor, self.name)
class SomeDecor(WithFileFields):
class Meta:
abstract = True
series = models.ForeignKey(SomeSeries) # some magic here to make ForeignKey to abstract model
texture = models.ImageField()
# -------------------------------------------
class PlinthSeries(SomeSeries): pass
class PlinthDecor(SomeDecor): pass
# Some magic to make PlinthDecor.series points to PlinthSeries
EDIT
Actually I don't want complicity of polymorphic relations, I want pure abstract models just to save typing (what abstract models are initially for). Suppose in my case the simplest way is to exclude ForeignKey from base model and type it only in all inherited models:
class SomeSeries(models.Model):
class Meta:
abstract = True
#...
class SomeDecor(WithFileFields):
class Meta:
abstract = True
series = None #?
#..
texture = models.ImageField()
def do_anything_with_series(self): pass
class PlinthSeries(SomeSeries): pass
class PlinthDecor(SomeDecor): pass
series = models.ForeignKey(PlinthSeries)
You can't create ForeignKey referencing abstract model. It's, even, doesn't make any sense, because ForeignKey translates into Foreign Key Constraint which have to reference existing table.
As a workaround, you can create GenericForeignKey field.
You can not do it because if you create two class inherit from your abstract class to what class your foreignkey should do? for first or for second?
So you need to create GenericForeignKey or not do any field and only after create model inherits from your abstract model add your foreign key.

Django - Can I alter construction of field defined in abstract base model for specific child model?

I am adding a slug to all my models for serialization purposes, so I have defined an abstract base class which uses the AutoSlugField from django_autoslug.
class SluggerModel(models.Model):
slug = AutoSlugField(unique=True, db_index=False)
class Meta:
abstract=True
I also have a custom manager and a natural_key method defined, and at this point I have about 20 child classes, so there are several things that make using an abstract base model worthwhile besides just the single line that defines the field.
However, I want to be able to switch a few of the default arguments for initializing the AutoSlugField for some of the child models, while still being able to utilize the abstract base class. For example, I'd like some of them to utilize the populate_from option, specifiying fields from their specific model, and others to have db_index=True instead of my default (False).
I started trying to do this with a custom Metaclass, utilizing custom options defined in each child Model's inner Meta class, but thats become a rat's nest. I'm open to guidance on that approach, or any other suggestions.
One solution would be to dynamically construct your abstract base class. For example:
def get_slugger_model(**slug_kwargs):
defaults = {
'unique': True,
'db_index': False
}
defaults.update(slug_kwargs)
class MySluggerModel(models.Model):
slug = AutoSlugField(**defaults)
class Meta:
abstract = True
return MySluggerModel
class MyModel(get_slugger_model()):
pass
class MyModel2(get_slugger_model(populate_from='name')):
name = models.CharField(max_length=20)
Update: I started out with the following solution, which was ugly, and switched to Daniel's solution, which is not. I'm leaving mine here for reference.
Here's my Metaclass rat trap that seems to be working (without extensive testing yet).
class SluggerMetaclass(ModelBase):
"""
Metaclass hack that provides for being able to define 'slug_from' and
'slug_db_index' in the Meta inner class of children of SluggerModel in order to set
those properties on the AutoSlugField
"""
def __new__(cls, name, bases, attrs):
# We don't want to add this to the SluggerModel class itself, only its children
if name != 'SluggerModel' and SluggerModel in bases:
_Meta = attrs.get('Meta', None)
if _Meta and hasattr(_Meta, 'slug_from') or hasattr(_Meta, 'slug_db_index'):
attrs['slug'] = AutoSlugField(
populate_from=getattr(_Meta, 'slug_from', None),
db_index=getattr(_Meta, 'slug_db_index', False),
unique=True
)
try:
# ModelBase will reject unknown stuff in Meta, so clear it out before calling super
delattr(_Meta, 'slug_from')
except AttributeError:
pass
try:
delattr(_Meta, 'slug_db_index')
except AttributeError:
pass
else:
attrs['slug'] = AutoSlugField(unique=True, db_index = False) # default
return super(SlugSerializableMetaclass, cls).__new__(cls, name, bases, attrs)
The SlugModel looks basically like this now:
class SluggerModel(models.Model):
__metaclass__ = SluggerMetaclass
objects = SluggerManager()
# I don't define the AutoSlugField here because the metaclass will add it to the child class.
class Meta:
abstract = True
And I can acheive the desired effect with:
class SomeModel(SluggerModel, BaseModel):
name = CharField(...)
class Meta:
slug_from = 'name'
slug_db_index = True
I have to put SluggerModel first in the inheritance list for models having more than one abstract parent model, or else the fields aren't picked up by the other parent models and validation fails; however, I couldn't decipher why.
I guess I could put this an answer to my own question, since it works, but I'm hoping for a better way since its a bit on the ugly side. Then again, hax is hax so what can you do, so maybe this is the answer.