2

I have been combing through the internet for quite some while without finding any solution to this problem.

What I am trying to do...

I have the following models:

class TrackingEventType(models.Model):
    required_previous_event = models.ForeignKey(TrackingEventType)

class TrackingEvent(models.Model):
    tracking = models.ForeignKey(Tracking)

class Tracking(models.Model):
    last_event = models.ForeignKey(TrackingEvent)

Now the main model is Tracking, so my admin for Tracking looks like this:

class TrackingEventInline(admin.TabularInline):
    model = TrackingEvent
    extra = 0

class TrackingAdmin(admin.ModelAdmin):
    inlines = [TrackingEventInline]

That's it for the current setup.

Now my quest:

In the TrackingAdmin, when I add new TrackingEvent inlines, I want to limit the options of TrackingEventType to onlye those, that are allowed to follow on the last TrackingEvent of the Tracking. (Tracking.last_event == TrackingEventType.required_previous_event).

For this, I would need to be able to access the related Tracking on the InlineTrackingEvent, to access the last_event and filter the options for TrackingEventType accordingly.

So I found this: Accessing parent model instance from modelform of admin inline, but when I set up TrackingEventInline accordingly:

class MyFormSet(forms.BaseInlineFormSet):
    def _construct_form(self, i, **kwargs):
        kwargs['parent_object'] = self.instance
        print self.instance
        return super(MyFormSet, self)._construct_form(i, **kwargs)


class MyForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        print kwargs
        self.parent_object = kwargs.pop('parent_object')
        super(MyForm, self).__init__(*args, **kwargs)

class TrackingEventInline(admin.TabularInline):
    form = MyForm
    formset = MyFormSet

    model = TrackingEvent
    extra = 0

I get a KeyError at /admin/.../tracking/2/change/ 'parent_object' from self.parent_object = kwargs.pop('parent_object')


Does anyone know how to solve this? Am I approaching the problem the wrong way? I guess this would be pretty easy in a custom form in the frontend, but I really want to use the admin, because the whole application is built to be used from the admin, and it would be a hell lot of work to build a custom admin interface just because of this problem :)

Community
  • 1
  • 1
platzhersh
  • 1,520
  • 20
  • 35
  • Ok so I found this: http://stackoverflow.com/questions/1883296/prepopulating-inlines-based-on-the-parent-model-in-the-django-admin and this: http://stackoverflow.com/questions/442040/pre-populate-an-inline-formset and will see where I can go from there.. – platzhersh Jun 22 '16 at 08:35

1 Answers1

1

Ok, so posting on StackOverflow is always helping to get the problem straight. I was able to put together a solution that works for me.

It includes defining my own Form in a outer function, as well as defining two InlineAdmin objects for TrackingEvent (one for update / edit, one just for insert).

Here's the code:

def create_trackingevent_form(tracking):
    """
    """
    class TrackingEventForm(forms.ModelForm):
        """
        Form for Tracking Event Inline
        """

        def clean(self):
            """
            May not be needed anymore, since event type choices are limited when creating new event.
            """
            next_eventtype = self.cleaned_data['event_type']
            tracking = self.cleaned_data['tracking']
            # get last event, this also ensures last_event gets updated everytime the change form for TrackingEvent is loaded
            last_eventtype = tracking.set_last_event()

            if last_eventtype:
                last_eventtype = last_eventtype.event_type

            pk = self.instance.pk
            insert = pk == None
            # check if the event is updated or newly created
            if insert:
                if next_eventtype.required_previous_event == last_eventtype:
                    pass
                else:
                    raise forms.ValidationError('"{}" requires "{}" as last event, "{}" found. Possible next events: {}'.format(
                        next_eventtype, 
                        next_eventtype.required_previous_event, 
                        last_eventtype,
                        '"%s" ' % ', '.join(map(str, [x.name for x in  tracking.next_tracking_eventtype_options()]))
                        )
                    )
            else:
                pass
            return self.cleaned_data

        def __init__(self, *args, **kwargs):
            # You can use the outer function's 'tracking' here
            self.parent_object = tracking

            super(TrackingEventForm, self).__init__(*args, **kwargs)
            self.fields['event_type'].queryset = tracking.next_tracking_eventtype_options()
            #self.fields['event_type'].limit_choices_to = tracking.next_tracking_eventtype_options()

    return TrackingEventForm


class TrackingEventInline(admin.TabularInline):
    #form = MyForm
    #formset = MyFormSet

    model = TrackingEvent
    extra = 0

    #readonly_fields = ['datetime', 'event_type', 'note']

    def has_add_permission(self, request):
        return False



class AddTrackingEventInline(admin.TabularInline):
    model = TrackingEvent
    extra = 0

    def has_change_permission(self, request, obj=None):
        return False

    def queryset(self, request): 
        return super(AddTrackingEventInline, self).queryset(request).none()

    def get_formset(self, request, obj=None, **kwargs):
        if obj:
            self.form = create_trackingevent_form(obj)
        return super(AddTrackingEventInline, self).get_formset(request, obj, **kwargs)

I hope this helps other people with the same problem.. Some credit to the Stack Overflow threads that helped me come up with this:

Prepopulating inlines based on the parent model in the Django Admin

Limit foreign key choices in select in an inline form in admin

https://docs.djangoproject.com/en/1.9/ref/models/instances/#django.db.models.Model.clean_fields

Please do not hesitate to ask questions if you have any

Community
  • 1
  • 1
platzhersh
  • 1,520
  • 20
  • 35