Search code examples
pythondjangodjango-modelsdjango-viewsdjango-forms

Django form not populating with POST data


Problem: Django form is populating with list of objects rather than values

Summary: I have 2 models Entities and Breaks. Breaks has a FK relationship to the entity_id (not the PK) on the Entities model.

I want to generate an empty form for all the fields of Breaks. Generating a basic form populates all the empty fields, but for the FK it generates a dropdown list of all objects of the Entities table. This is not helpful so I have excluded this in the ModelForm below and tried to replace with a list of all the entity_ids of the Entities table. This form renders as expected.

class BreakForm(ModelForm):   
    class Meta:
        model = Breaks
        #fields = '__all__'
        exclude = ('entity',)
    
    def __init__(self, *args, **kwargs):        
        super(BreakForm, self).__init__(*args, **kwargs)         
        self.fields['entity_id'] = ModelChoiceField(queryset=Entities.objects.all().values_list('entity_id', flat=True))    

The below FormView is the cbv called by the URL. As the below stands if I populate the form, and for the FK column entity_id choose one of the values, the form will not submit. By that field on the form template the following message appears Select a valid choice. That choice is not one of the available choices.

class ContactFormView(FormView):
    template_name = "breaks/test/breaks_form.html"
    form_class = BreakForm

My initial thoughts were either that the datatype of this field (string/integer) was wrong or that Django needed the PK of the row in the Entities table (for whatever reason).

So I added a post function to the FormView and could see that the request.body was populating correctly. However I can't work out how to populate this into the ModelForm and save to the database, or overcome the issue mentioned above.

Addendum:

Models added below:

class Entity(models.Model):
    pk_securities = models.AutoField(primary_key=True)
    entity_id = models.CharField(unique=True)
    entity_description = models.CharField(blank=True, null=True)
    
    class Meta:
        managed = False
        db_table = 'entities'
    
    
class Breaks(models.Model):
    pk_break = models.AutoField(primary_key=True)
    date = models.DateField(blank=True, null=True)    
    entity = models.ForeignKey(Entity, on_delete= models.CASCADE, to_field='entity_id')  
    commentary = models.CharField(blank=True, null=True)
    active = models.BooleanField()

    def get_absolute_url(self):
        return reverse(
            "item-update", args=[str(self.pk_break)]
        )

    def __str__(self):
        return f"{self.pk_break}"

    class Meta:
        managed = False
        db_table = 'breaks'

Solution

  • SOLUTION

    Firstly I got this working by adding the following to the Entity Model class. However I didn't like this as it would have consequences elsewhere.

    def __str__(self):
            return f"{self.entity_id}"
    

    I found this SO thread on the topic. The accepted answer is fantastic and the comments to it are helpful. The solution is to subclass ModelChoiceField and override the label_from_instance

    class EntityChoiceField(ModelChoiceField):
        def label_from_instance(self, obj):
            return obj.entity_id