Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use Model Property in a Django Model Form

I am attempting to use model properties like fields within a model form, but so far haven't had any luck. The result is that the form renders only the model fields, and not the property I defined. Any idea how to get the form to recognize the property added to the model? I expect to see the latitude property added as just another field in the form.

Models.py:

class Plot(models.Model):
        plot_id = models.AutoField(primary_key=True)
        plot_number = models.IntegerField(validators=[MinValueValidator(1)], null=False, unique=True)
        geometry = models.PointField(srid=2163, null=True, blank=True) 
        objects = models.GeoManager()

        @property
        def latitude(self):
            self.geometry.transform(4326)
            return self.geometry.y

        @latitude.setter
        def latitude(self, latitude):
            self.geometry.y = latitude

Forms.py:

class InventoryPlotForm(ModelForm):
    class Meta:
        model = ForestInventoryPlot
        exclude = {"geometry"}
like image 603
akthor Avatar asked Feb 27 '14 07:02

akthor


People also ask

What is __ str __ in Django?

str function in a django model returns a string that is exactly rendered as the display name of instances for that model.

What is a Django model property?

Model: A model is the single, definitive source of data about your data. It contains the essential fields and behaviours of the data you're storing. Generally, each model maps to a single database table.

Can we inherit model in Django?

Models inheritance works the same way as normal Python class inheritance works, the only difference is, whether we want the parent models to have their own table in the database or not. When the parent model tables are not created as tables it just acts as a container for common fields and methods.

How to create a form from a model in Django?

For this reason, Django provides a helper class that lets you create a Form class from a Django model. The generated Form class will have a form field for every model field specified, in the order specified in the fields attribute. Each model field has a corresponding default form field.

Why can’t I create another model field called author in Django?

In Django, this isn’t usually permitted for model fields. If a non-abstract model base class has a field called author, you can’t create another model field or define an attribute called author in any class that inherits from that base class. This restriction doesn’t apply to model fields inherited from an abstract model.

Can I use commoninfo model as a base class in Django?

Instead, when it is used as a base class for other models, its fields will be added to those of the child class. The Student model will have three fields: name, age and home_group. The CommonInfo model cannot be used as a normal Django model, since it is an abstract base class.

How to save a model with missing fields in Django?

Django will prevent any attempt to save an incomplete model, so if the model does not allow the missing fields to be empty, and does not provide a default value for the missing fields, any attempt to save () a ModelForm with missing fields will fail.


3 Answers

Some years later. A complete answer, working in Django up to 2.2. As others have pointed out, only real db fields are included in the model form. So, you'll need to:

  • Define a custom model form, add your @property field
  • Exclude your geometry field
  • In the __init__ of the form, get the value, and set it as initial.
  • Customize your save method (on the form or admin)

Note: This works also in more complex cases, where you want to abstract away some more complex database structure...


class InventoryPlotForm(ModelForm):

    class Meta:
        model = ForestInventoryPlot
        exclude = ("geometry", )

    latitude = forms.WhateverField()

    def __init__(self, *args, **kwargs):
        instance = kwargs.get('instance', None)
        if instance:
            kwargs['initial'] = {'latitude': instance.latitude, }
        super().__init__(*args, **kwargs)

    def save(self, *args, **kwargs):
        self.instance.latitude = self.cleaned_data['latitude']
        return super().save(*args, **kwargs)

like image 129
benzkji Avatar answered Oct 15 '22 17:10

benzkji


Your property is not a Field instance, and as such a ModelForm cannot automatically generate a form field for it. You will need to explicitly define a latitude field on the InventoryPlotForm and manage its retrieval and update in the form's __init__ and save methods. Alternatively, you can implement your own Widget class and tell your InventoryPlotForm to use it for the geometry field:

class InventoryPlotForm(ModelForm):
    class Meta(object):
        widgets = {
            'geometry': MyGeometryWidget,
        }
        ...
    ...
like image 35
lanzz Avatar answered Oct 15 '22 17:10

lanzz


Only Django Fields are automatically generated by ModelForm.

  • You need to define your field in the ModelForm
  • Probably make geometry field hidden
  • Write custom ModelForm Save method and separately save model fields and model properties.
  • Modify your view to set the initial for the ModelForm

For example something like this:

class InventoryPlotForm(ModelForm):
    latitude = forms.CharField(max_length=52)           # Something like this

    def save(self, commit=True):
        model_instance = super(InventoryPlotForm, self).save(commit=False)
        result = super(InventoryPlotForm, self).save(commit=True)
        model_instance.latitude = self.cleaned_data['latitude']
        model_instance.save()
        return result

    class Meta:
        model = ForestInventoryPlot
        widgets = {'geometry': HiddenInput()}

in your view

...
form = InventoryPlotForm(instance=forestinventoryplot,
                         initial={"latitude":forestinventoryplot.latitude})
...
like image 45
fragles Avatar answered Oct 15 '22 16:10

fragles