Question

I have 3 Models:

class FileType(models.Model):
    name=models.CharField(max_length=128)

class ManagedFile(models.Model):
    type = models.ForeignKey(FileType)
    content = models.FileField(upload_to=path_maker)

class Tag(models.Model):
    type = models.ForeignKey(FileType)
    m_file = models.ForeignKey(ManagedFile)

    def clean(self):
        if self.m_file is None:
            return
        if self.type != self.m_file.type:
            raise ValidationError("File type does not match Tag type")

When select an m_file for a tag, the m_files type MUST match the Tags type. This is all well and good, but the admin drop down for Tag.m_file shows files of all types, regardless of the Tag's type. This is Confusing to users.

There seem to me a number of ways to filter the drop down statically. So if I wanted to say that we will never let the user see Type.pk=1 in the dropdown, I can to that. But there does not seem to be a way to filter on m_file.Type == Self.Type

Was it helpful?

Solution

It is actually quite easy to create your admin form classes dynamically. Something like this should work:

def tagform_factory(filetype):
    class TagForm(forms.ModelForm):
        m_file = forms.ModelChoiceField(
            queryset=ManagedFile.objects.filter(type=filetype)
        )
    return TagForm


class TagAdmin(admin.ModelAdmin):

    def get_form(self, request, obj=None, **kwargs):
        if obj is not None and obj.type is not None:
            kwargs['form'] = tagform_factory(obj.type)
        return super(TagAdmin, self).get_form(request, obj, **kwargs)

Note that the get_form method is responsible for building the form class, not the form instance. It is badly named, IMHO.

However, you still need to decide what to do for forms that are used to add new tags, rather than edit existing ones. In that case you do not yet have a type to which you can restrict the dropdown. Maybe there is actually a data modeling problem lurking here? Do you really need the type field on the Tag model? Maybe it should just be removed?

OTHER TIPS

Try overriding formfield_for_foreignkey() in the admin. I'm not 100% it does what you want it to do, but if not it should get you started:

class TagAdmin(admin.ModelAdmin):
    ...
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        kwargs['queryset'] = Tag.objects.filter(type=self.type)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top