How to iterate formsets with inline structure

977 Views Asked by At

I face up with the problem of inability to iterate through my formsets. I have already create an inline structure between Client model(form) and Phone model(formset). My aim is to be able to store more than one phone for each client.

The issue is that despite the fact that I can create more than one inlines, when it comes the phase of storing, my code only stores the last phone and not each phone as expected.

models.py

class Client(models.Model):
    name = models.CharField(max_length=50, verbose_name="Όνομα")
    surname = models.CharField(max_length=50, verbose_name="Επίθετο")
    amka_amesa = models.BigIntegerField( validators=[MaxValueValidator(99999999999)],null=True,blank=True, verbose_name="AΜΚΑ Άμεσα Ασφαλισμένου")
    amka_emesa = models.BigIntegerField( validators=[MaxValueValidator(99999999999)], unique=True, null=True, blank=True, verbose_name="ΑΜΚΑ Έμεσα Ασφαλισμένου")
    am_asfa = models.CharField( max_length=11, validators=[RegexValidator(r'^\d+$')], null=True, blank=True, verbose_name="Α.Μητρώου Ασφαλισμένου")
    address = models.CharField(max_length=100, null=True, blank=True, verbose_name="Διεύθυνση")
    ika_branch = models.ForeignKey(Client_IKA_Branch, null=True, blank=True, verbose_name=u'Υποκατάστημα Ασφαλισμένου')
    notes = models.CharField(max_length=100, null=True, blank=True, verbose_name="Σημειώσεις")
    #active field=1
    #inactive field=0
    INACTIVE, ACTIVE = range(0, 2)
    ACTIVITY_OPTIONS = (
        (INACTIVE, 'Ανενεργός'),
        (ACTIVE, 'Ενεργός'),
        )
    activity = models.IntegerField(choices=ACTIVITY_OPTIONS, null=True,default=ACTIVE)

class Phone(models.Model):
    MOBILE, WORK, HOME = range(0, 3)
    CATEGORIES = (
        (MOBILE, 'Κινητό'),
        (WORK, 'Δουλειά'),
        (HOME, 'Σπίτι'),
        )
    number = models.CharField(max_length=14, null=True)
    category = models.IntegerField(choices=CATEGORIES, null=True,default=MOBILE)
    client = models.ForeignKey(Client, null=True)

    def __unicode__(self):
        return self.number

    def get_absolute_url(self):
        return reverse('client_list')

forms.py

class ClientForm(ModelForm):
    class Meta:
        model = Client
        exclude=('activity',)

class PhoneForm(ModelForm):
    class Meta:
        model = Phone
        exclude = ('client',)

ClientPhoneFormSet=inlineformset_factory(Client,Phone,form=PhoneForm,extra=1) 

views.py

class ClientCreateView(LoginRequiredMixin, CreateView):
    model = Client
    form_class = ClientForm
    template_name='test/test_client_phone.html'

    def get_success_url(self):         
        return reverse('client_edit', args=(self.object.id,))

    def form_valid(self, form):
        ctx = self.get_context_data()
        PhoneFormSet = ctx['PhoneFormSet']
        if PhoneFormSet.is_valid() and form.is_valid():
            self.object = form.save() # saves Father and Children
            phones = PhoneFormSet.save(commit=False) 
            for instance in phones:                 
                instance.save()
            return redirect(self.get_success_url())
        else:
            return self.render_to_response(self.get_context_data(form=form))

    def form_invalid(self, form):
        return self.render_to_response(self.get_context_data(form=form))

    def get_context_data(self, **kwargs):
        ctx = super(ClientCreateView, self).get_context_data(**kwargs)
        if self.request.POST:
            ctx['form'] = ClientForm(self.request.POST)
            ctx['PhoneFormSet'] = ClientPhoneFormSet(self.request.POST)
        else:
            ctx['form'] = ClientForm()
            ctx['PhoneFormSet'] = ClientPhoneFormSet()
        return ctx

template

{% extends 'base.html' %}
{% load bootstrap3 %}
{% load static %}
<!-- Latest compiled and minified JavaScript -->


{% block content %}

<div class="col-md-12 text-center">
<h2>Δημιουργία / Αλλαγή Πελάτη </h2>
</div>

<hr>
<form class="well" method="post" action="">
{% csrf_token %}
{% bootstrap_form form %}

<table class="table phone">
  {{ PhoneFormSet.management_form }}

  {% for form in PhoneFormSet.forms %}
      {% if forloop.first %}
          <thead>
          <tr>
              {% for field in form.visible_fields %}
                  <th>{{ field.label|capfirst }}</th>
              {% endfor %}
          </tr>
          </thead>
      {% endif %}
      <tr class="{% cycle row1 row2 %} formset_row">
          {% for field in form.visible_fields %}
              <td>
                  {# Include the hidden fields in the form #}
                  {% if forloop.first %}
                      {% for hidden in form.hidden_fields %}
                          {{ hidden }}
                      {% endfor %}
                  {% endif %}
                  {{ field.errors.as_ul }}
                  {{ field }}
              </td>
          {% endfor %}
      </tr>
  {% endfor %}
</table>


<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="{% static 'jquery.formset.js' %}"></script>
<script type="text/javascript">
  $('table.phone tr.formset_row').formset({
      addText: 'Πρόσθεσε Τηλέφωνο',
      deleteText: 'Διαγραφή',
      prefix: 'client_phone_set',
      animateForms: true
  });
</script>

    {% buttons %}
      <button type="submit" class="btn btn-primary">
         Submit
      </button>
    {% endbuttons %}
</form>


<hr>
{{ form.media }}
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/forms.css' %}"/>
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/base.css' %}"/>
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/widgets.css' %}"/>
<script type="text/javascript" src="/admin/jsi18n/"></script>
<script type="text/javascript" src="/static/admin/js/core.js"></script>
<script type="text/javascript" src="/static/admin/js/admin/RelatedObjectLookups.js"></script>
<script type="text/javascript" src="/static/admin/js/vendor/jquery/jquery.min.js"></script>
<script type="text/javascript" src="/static/admin/js/jquery.init.js"></script>
<script type="text/javascript" src="/static/admin/js/actions.min.js"></script>
<script type="text/javascript" src="/static/admin/js/calendar.js"></script>
<script type="text/javascript" src="/static/admin/js/admin/DateTimeShortcuts.js"></script>

{% endblock %}

I noticed that the phones = PhoneFormSet.save(commit=False) line returns only one instance. Why is this happening?

Any help will be appreciated.

2

There are 2 best solutions below

2
On

Try changing

phones = PhoneFormSet.save(commit=False) for instance in phones: instance.save()

into

phones = PhoneFormSet.save(commit=False) 
phones.instance=self.object
phones.save()
0
On

The fix it has to do with the prefix in template. It has to be prefix: 'phone_set', and not prefix: 'client_phone_set', as I had it before.