Overriding a parent's form field with options? - python

I have a base class that is a Django form. The child class then inherits from the parent including all the fields, but I need to make changes to one of the parent's field option such as label, required, and etc.
Example:
class BaseForm(forms.Form):
userid = forms.CharField(required=True)
class ChildForm(BaseForm):
# I need to change the parent field option
userid = forms.CharField(required=False)
Any suggestions?

You're doing exactly what you should do.
It's particularly fitting in this case because that's the exact pattern for overriding ModelForm fields.
If you need to retain properties you don't know about / are outside your control (or what have you), you could override the __init__ method and access the form fields via self.fields['myfield']
class ChildForm(BaseForm):
def __init__(self, *args, **kwargs):
super(ChildForm, self).__init__(*args, **kwargs)
self.fields['userid'].required = False

Related

How to delete constant in subclass?

Let's say I have a class something like the following:
class PostSerializer(serializers.HyperlinkedModelSerializer):
updated_at = serializers.DateTimeField()
def __init__(self, *args, **kwargs):
init = super().__init__(*args, **kwargs)
return init
I want to create a subclass of the PostSerializer class and I'd like to remove the updated_at constant property from the subclass-ed class.
class PostWithoutUpdatedAtSerializer(serializers.HyperlinkedModelSerializer):
# something to remove the updated_at property ?
def somefunc(self);
pass
I use a framework for example django so generally I cannot simply remove the property from the parent class, I need to subclass them. And of course obviously I need to "delete" the property, I cannot do updated_at = None, it's not a deleting.
How is it possible? Thanks.
It's not directly possible, since the attribute doesn't exist on your derived class at all (it does on the superclass), so there's nothing to remove or reassign.
Instead, the framework you're using (Django REST Framework, my magic ball tells me), uses a metaclass that inspects the class definition for field objects and puts them into cls._declared_fields on the class (along with any fields from the superclass(es)).
The real fields for your serializer instance are acquired by get_fields(), which by default just copies _declared_fields.
In other words, if your Django REST Framework serializer subclass should not serialize that field, customize get_fields():
def get_fields(self):
fields = super().get_fields()
fields.pop("updated_at", None) # remove field if it's there
return fields

Modify django model form field with widget

I want to modify some fields in model form, and i found two methods:
First Method:
class ProfileForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['bio'].widget.attrs.update({'placeholder': 'Enter your bio here'})
class Meta:
model = Profile
fields = ['bio']
Second Method:
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['bio']
widgets = {
'bio': Textarea(attrs={'placeholder': 'Enter your bio here'})
I just want to know if they are the same?
Which is better?
Or is there another better way?
Thankyou.
Method 1
The first method calls the constructor super().__init__(*args, **kwargs) prior to manipulating the field. This enables developers to build the class in a default state and then play around with the classes components (attributes, functions).
The first method is most commonly used if the developer cannot achieve the results they want within the second method. This is because you're moving away from configuration to more of a manipulation of the class.
Method 2
The second method allows developers to define the configuration of the class before it is instantiated. This generally provides better usability and readability to other developers.
EXAMPLE:
Say you want your field bio to be a required for all general users except for superusers.
class ProfileForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop(user, None)
super().__init__(*args, **kwargs)
if self.user.is_superuser():
self.fields['bio'].required = False
class Meta:
model = Profile
fields = ['bio']
Using this method you can allow your Profile model's bio field attributes to be transposed to a form field upon instantiating and then making a small tweak to determine whether it's required for that particular user. This can be done without redefining the whole field.
Note: The form call in the GET request would look like ProfileForm(user=request.user)

How to change the fields displayed if an instance is passed to a form?

I have a form something like this:
class ExampleForm(forms.ModelForm):
class Meta:
model = Example
fields = ('field1','field2')
Now, I want to add an additional field if an instance is passed to it. Which means that, if I create an form with an instance (for editing that object) I want to change the fields being displayed.
I know I can use create an another form for this purpose and create an instance of that rather than using this. However, is there a way to do this in this same form?
Override the __init__ method, and modify self.fields when instance is passed to the form.
class ExampleForm(forms.ModelForm):
class Meta:
model = Example
fields = ('field1','field2')
def __init__(self, *args, **kwargs):
super(ExampleForm, self).__init__(*args, **kwargs)
if kwargs.get('instance'):
self.fields['field3'] = forms.CharField()

Automatically create child object from parent - Django 1.11

I have a parent class:
class Parent(models.Model):
field1 = model.CharField()
field2 = model.CharField()
And a child:
class Child1(Parent):
pass
Is there a possible way to create a child object whenever a perent is saved?
The child inherits all the fields from the parent, but, regardless if filled or not, I would need to create a new child object whenever a parent is saved.
Any ideas?
You can use signals or you can override save method of Parent model to do that.
#receiver(models.signals.post_save, sender=Parent)
def post_parent_save(sender, instance, created, *args, **kwargs):
# Create child here
Or,
class Parent(models.Model):
def save(self, *args, **kwargs):
super(Parent, self).save(*args, **kwargs)
# Create child here
In both options, if you want to create a child only when a parent is created (not updated), you need to add extra login. For signals, you can use the created parameter, for overriding save method, you need to check if the model instance has an id field before calling super save method.
I'm wondering if something like this would work for you:
class Child1(Parent):
class Meta:
db_table = 'whateverappparentisin_parent'
managed = False
I'm not sure what Django would do with this, but the idea is that you get a model with the exact same fields, backed by the same database table (so everything else e.g. deletes on Parent would also immediately be "visible" on Child1), without Django wanting to make migrations for it.
But I don't know if it's allowed.

Custom Django ForeignKey field to a defined model class

Using Django 1.11 I'd like to make a custom model field that inherits from ForeignKey.
With a normal ForeignKey, you can do something like the following:
class Car(models.Model):
manufacturer = models.ForeignKey(Company)
Instead, I'd like to make a model field that is largely the same as the ForeignKey field, but 1) uses a different default widget for the formfield and 2) doesn't require the model name to be placed as a positional parameter.
# myapp/models.py
from otherapp.fields import ManufacturerField
class Car(models.Model):
manufacturer = ManufacturerField()
Unfortunately, I'm having a hard time overriding the init method of the child class to get my "Company" model inserted into the mix. Here's what I have so far by way of a modelfield (not working on the widget at all yet):
# otherapp/fields.py
from otherapp.models import Company
class ManufacturerField(models.ForeignKey):
def __init__(self, *args, **kwargs):
return super(ContentField, self).__init__(Company, **kwargs)
When I try to do this, I get:
TypeError: Couldn't reconstruct field manufacturer on myapp.Car: __init__() got multiple values for argument 'to'
Is there a property I can set on the custom modelfield class to specify that I want this to be a foreignkey to one specific model? If not, any ideas on how I can properly intercept the init method to feed in my model?
super() referring to the base class explicitly. you can see this question to understand it.
def __init__(self, *args, **kwargs):
return super(ContentField, self).__init__(Company, **kwargs)
should be;
def __init__(self, *args, **kwargs):
return super(ManufacturerField, self).__init__(*args, **kwargs)
Here is an example how to custom the field.

Categories