Custom Django ForeignKey field to a defined model class - python

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.

Related

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()

Django Pickle Field Access to its model instance on load

I have a custom django field subclass for storing my own pickled classes. Is there any way I could set a model attribute that points to the model instance on my pickled class on each load from the database?
So far my best guess is in the unpickling process, inside the to_python method, but I'm not sure if the Field has a reference to the model instance or class.
EDIT 1: The model reference inside of the to_python method is indeed a reference to the class, not the instance
Figured it out!
I overrode the model's __init__ method like this:
class MyModel(models.Model):
def __init__(self, *args, **kwargs):
# Don't do any extra looping or anything in here because this gets called
# at least once for every row in each query of this table
self._meta.fields[2].model_instance = self
super(MyModel, self).__init__(*args, **kwargs)
field1 = models.TextField()
field2 = models.PickleField()
field3 = models.DateTimeField()
Then in my field subclass:
def to_python(self, value):
# logic and unpickling, then right before your return:
if hasattr(self, 'model_instance'): # avoid AttributeError if list, dict, etc.
value.model_instance = self.model_instance
return value

Django abstract model inheritance

In a model I usually put a "uuid" field for friendly URI, also a "slug" field.
Say I have a model named "SomeModel", by overriding its save() method, I can generate a uuid and a slug when it's being saved:
class SomeModel(models.Model):
...
def save(self, *args, **kwargs):
if not self.uuid:
uuid = shortuuid.uuid()[:10]
while SomeModel.objects.filter(uuid=uuid).exists():
uuid = shortuuid.uuid()[:10]
self.uuid = uuid
if not self.slug:
self.slug = slugify(self.title)[:500].rstrip('-')
super(SomeModel, self).save(*args, **kwargs)
It works well on regular model. Now I'd like to have an abstract model:
class SomeAbstractModel(models.Model):
class Meta:
abstract = True
def save(self, *args, **kwargs):
...
And then:
class SomeModel(SomeAbstractModel):
class Meta(SomeAbstractModel.Meta):
...
The problem is, in the abstract model, looks like I cannot just simply replace
while SomeModel.objects.filter(uuid=uuid).exists():
with
while SomeAbstractModel.objects.filter(uuid=uuid).exists():
because abstract model doesn't have a manager.
I was wondering in this case, how can I avoid having redundant code in all models' save() methods. Also I'm not sure if
while SomeModel.objects.filter(uuid=uuid).exists():
is the best practice to check if an uuid exists or not.
Not sure if it is the prettiest way in town but this should work:
while self.__class__.objects.filter(...):
pass
When you create SomeModel(SomeAbstractModel), just create the class Meta from scratch without inheriting. By inheriting vom SomeAbstractModel.Meta you make it abstract again, and you cannot query on abstract model, not because they have no manager, but because there are no tables created.
So either you do this:
class SomeModel(SomeAbstractModel):
...
class Meta(SomeAbstractModel.Meta):
abstract=False
... your other model specific options
Or you do this (if you do not have any other model specific options:
class SomeModel(SomeAbstractModel):
...

Overriding a parent's form field with options?

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

Categories