LXj
LXj

Reputation: 413

When subclassing a model field, __init__'s arguments are not processed

I have a subclass of models.ForeignKey, the only purpose of which is to use a custom widget:

from django.db import models
from .models import Images

class GAEImageField(models.ForeignKey):
    def __init__(self, **kwargs):
        super(GAEImageField, self).__init__(Images, **kwargs)

    def formfield(self, *args, **kwargs):
        field = forms.ModelChoiceField(self, widget=ImageUploadWidget, *args, **kwargs)
        field.queryset = Images.objects.all()
        return field

The problem is, when I try to use this field, any parameters to __init__ are ignored. For instance, if I try this model:

class SomethingWithImage(models.Model):
    name = models.CharField('Awesome name', max_length=64)
    image = GAEImageField(verbose_name='Awesome image', blank=True)

...despite the fact I specified verbose_name, the label on a generated form will be "Image" and trying to specify empty value will raise an error even though I use blank=True

Upvotes: 0

Views: 158

Answers (1)

koniiiik
koniiiik

Reputation: 4382

Well, the problem is with your formfield method. If you look at the implementation used in the default ForeignKey for example, you'll see it calls super. I'd recommend something like this:

def formfield(self, **kwargs):
    defaults = {
        'widget': ImageUploadWidget,
    }
    defaults.update(kwargs)
    return super(GAEImageField, self).formfield(**defaults)

The problem is, form fields don't extract any information from model fields. Form fields can be used completely independently from models, they don't have to be necessarily backed by a model field. That means all settings, like whether the field is required or not, its label etc. have to be passed as parameters to their constructors. It is the responsibility of the model field to create an appropriate instance of a form field, all settings included. The default implementation of django.db.models.fields.Field.formfield takes care of that which is why you usually want to call the parent's method.

As for the blank issue, try also setting null=True, otherwise even though the form will accept a blank value, the database will reject it. Also, note that modifying the value of null after syncdb has been run requires a database migration.

Upvotes: 1

Related Questions