I have an abstract BaseModel from which many models will inherit the behavior to initialize attributes values from the constructor:
class BaseModel(models.Model):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for key in kwargs:
setattr(self, key, kwargs[key])
class Meta:
abstract = True
Then, I try to initialize one of its specializations like this:
BaseModelSon(attr1=val1, attr2=val2, ...)
Is this a good practice? Do you know any other way to achieve this dynamic initialization for Django models?
You can already create regular Django model instances with
MyModel(field1='value1', field2='value2')
so I don't think your __init__ method is necessary.
Also you'll probably find useful Model.objects.create() method for new Django models instances creation:
instance = MyModel.objects.create(field1='value1', field2='value2'...)
If there are some auto-generated fields (for example, id or some date with auto_now_add=True option) — you'll get their values in instance if you have used Model.objects.create().
Related
I want to have a FilterSet class that allows all of the fields to be filtered as a list.
I was thinking of overriding the default FilterSet class of django-filters and having the fields be dynamically set as well as setting a method to each one.
So, something like this for example:
ModelName_fields = {'names': 'name', 'ids': 'id', 'second_ids': 'second_id'}
class ListFilter(FilterSet):
def __init__(self, *args, **kwargs):
# self._meta.fields = [field_name for field_name in modelNameHere + '_fields']
super(FilterSet).__init__(*args)
class SomeAPIView(mixins.ListModelMixin):
model = ModelName
filterset_class = ListFilter
Basically, ModelName_fields is a declared constant that maps the query parameter to the field name of the model. In this case, I declare the model on the view as well as the filterset class and in the __init__ method of the filterset class, I dynamically attach the fields as well as the query parameter name.
In all essence, I just want to make the ListFilter as generic as possible to be used on different views as well.
My question is, is this the correct way or is there some other better way to accomplish this? Also, how can I get the name of the model, which is an attribute of the view class, in the ListFilter class?
Let's say I have a proxy user model as
class UserWithProfile(User):
profile_description = models.TextField()
class Meta:
proxy = True
ordering = ('first_name', )
I want to make certain that all data which could in the future be associated with a UserWithProfile entry is deleted when this profile is deleted. In other words I want to guarantee the on_delete behavior of all existing and future ForeignKey fields referencing this model.
How would one implement either a test checking this, or raise an error when another on_delete behavior is implemented?
I know it would be possible to make a custom ForeignKey class, which is what I will be probably doing, ...
class UserWithProfileField(models.ForeignKey):
def __init__(self, *args, **kwargs):
kwargs.setdefault('to', UserWithProfile)
kwargs.setdefault('on_delete', models.CASCADE)
super().__init__(*args, **kwargs)
... however that couldn't stop future users from using the ForeignKey class with a different on_delete behavior.
Instead of setdefault, you can override the on_delete parameter:
class UserWithProfileField(models.ForeignKey):
def __init__(self, *args, **kwargs):
kwargs['to'] = UserWithProfile
kwargs['on_delete'] = models.CASCADE
super().__init__(*args, **kwargs)
regardless what the user will now use for to=… or on_delete=…, it will use UserWithProfile and CASCADE.
Strictly speaking one can of course still try to alter the attributes of the ForeignKey, but that is more complicated, especially since Django constructs a ForeignObjectRel object to store relation details.
Note that a proxy model [Django-doc] is not used to add exta fields to the model. THis is more to alter the order, etc. and define new/other methods.
I don't get the invariants you are starting out with:
It's irrelevant whether you want to delete references to User or UserWithProfile since these are the same table?
You cannot police what other tables and model authors do and in which way shape or form they point to this table. If they use any kind of ForeignKey that's fine, but they could also point to the table using an unconstrained (integer?) field.
Could you make a test that bootstraps the database and everything, iterates over all models (both in this app and others) and checks every ForeignKey that is there to see if it is pointing to this model and it is setup correctly? That should serve the intended goal I believe.
I've got an abstract model in my project that I want to use to define a field by default on concrete subclasses, but also to allow that field to be redefined as something other than the default dynamically. All of this works right now:
class classproperty(object):
"""
Decorator for making class properties
"""
def __init__(self, fget):
self.fget = fget
def __get__(self, owner_self, owner_cls):
return self.fget(owner_cls)
class BaseModel(models.Model):
class Meta(object):
abstract = True
#classproperty
def _special_attribute_field(self):
return getattr(self, '_bm_special_attribute_field', 'default')
#property
def bm_special_attribute(self):
return getattr(self, self._special_attribute_field)
...and then there are a bunch of methods that use the latter two functions to figure out which field to access.
The problem is that right now, classes inheriting from BaseModel have to define the default field explicitly, even if they don't use _bm_special_attribute_field to specify something other than the default. What I'd like to do is programmatically define default on concrete submodels only if those models don't use _bm_special_attribute_field to change it to something else, in which case, they should bring their own field. Is there a way to do this, perhaps with metaclasses? The key thing being that it has to not muck up the Django machinery.
Suppose I want to create and update a model. What fields are displayed and the type of validation depends on the action (create or update). But they still share a lot of the same validation and functality. Is there a clean way to have a ModelForm handle this (besides just if instance exists everywhere) or should I just create two different model forms?
Two possibilities spring to mind. You could set an attribute in the form's __init__ method, either based on a parameter you explicitly pass in, or based on whether self.instance exists and has a non-None pk:
class MyModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
# either:
self.edit = kwargs.pop('edit', False)
# or:
self.edit = hasattr(self, instance) and self.instance.pk is not None
super(MyModelForm, self).__init__(*args, **kwargs)
# now modify self.fields dependent on the value of self.edit
The other option is to subclass your modelform - keep the joint functionality in the base class, then the specific create or update functionality in the subclasses.
When using Model class like this:
class MyModel(models.Model):
def __init__(self, *args, **kwargs):
self.myfield = models.Field()
super(MyModel, self).__init__(*args, **kwargs)
It doesn't take into consideration myfield(in the admin form, when saving the object... )
But if i declare like that:
class MyModel(models.Model):
myfield = models.Field()
It works just fine.
Why?
Edit
I think i have a good reason: I have an abstract class UploadItem that defines a field called file like this: self.file = models.FileField(upload_to=upload_to) As you can see, in each child class, i have to call parent init method with appropriate upload_to variable(say 'videos' for Video model). So i cannot do it the normal way.
Because the Django ORM code does some serious meta-magic during class definition (just browse the django/db code to see how magic). You are doing an end-run around that magic by creating fields on the fly in the __init__() function.
Is there a really good reason for not creating the class in the normal way? If not, then do it the normal way. If you do have a good reason then get ready to get into the really deep end of the pool -- both of Python and Django.
Setting a dynamic path for the upload_to attribute is absolutely not a good reason for wanting to muck around with model field declaration.
This is something that Django handles already - if you set upload_to to a callable, you can return the correct value dependent on the model instance. See the documentation.