Dicky Raambo
Dicky Raambo

Reputation: 523

django ListView with a form

I have a CBV that use ListView at first here my views.py

class InventoryListView(ListView):
    context_object_name = 'inventorys'
    model = models.Inventory

and here my template_list.html

{% for inventory in inventorys %}
<tr>
  <td>{{ inventory.name }}</td>
  <td>{{ inventory.sn }}</td>
  <td>{{ inventory.employee.name }}</td>
  <td>{{ inventory.desc }}</td>
</tr>
{% endfor %}

it returns all the data as expected.

but I need add form with it. and then add some of code to my views.py

class InventoryListView(ListView):
    template_name ='system/inventory_list.html'
    context_object_name = 'inventorys'
    model = models.Inventory

    def get(self, request):
        form = InventoryForm()
        return render(request, self.template_name, {'form': form})

    def post(self, request):
        form = InventoryForm(request.POST)

and here my forms.py

class InventoryForm(forms.ModelForm):

    name = forms.CharField(max_length=255)
    sn = forms.DecimalField(max_digits=20, decimal_places=0)
    desc = forms.CharField(widget=forms.Textarea)
    employee = forms.ModelChoiceField(queryset=Employee.objects.all(), to_field_name="id")

    class Meta:
        model = Inventory
        fields = ('name', 'sn', 'desc', 'employee')

and here my template_list.html

{% for inventory in inventorys %}
<tr>
  <td>{{ inventory.name }}</td>
  <td>{{ inventory.sn }}</td>
  <td>{{ inventory.employee.name }}</td>
  <td>{{ inventory.desc }}</td>
</tr>
{% endfor %}

<form method="post" action="{% url 'system:inventory_create' %}">
  {% csrf_token %}
    {{ form.as_p }}

    <input type="submit" value="Submit">
</form>

now form working perfectly and checked on my DB its submitted. but list of data not showing like before, because I add:

    def get(self, request):
        form = InventoryForm()
        return render(request, self.template_name, {'form': form})

to my views.py

so how to make both works, list of data & form.

Upvotes: 7

Views: 12377

Answers (4)

To save the state of the form

    def get_context_data(self, **kwargs):
       context = super(RecentChannels, self).get_context_data(**kwargs)
       context['form'] = RecentChannelsForm(self.request.GET)
       return context

Upvotes: 1

Alasdair
Alasdair

Reputation: 308949

Try to avoid overriding get or post for generic class-based-views. It's easy to end up duplicating existing functionality or having to repeat code.

In this case, you can add the form to the template context by overriding the get_context_data method.

class InventoryListView(ListView):
    template_name ='system/inventory_list.html'
    context_object_name = 'inventorys'
    model = models.Inventory

    def get_context_data(self, **kwargs):
        context = super(InventoryListView, self).get_context_data(**kwargs)
        context['form'] = InventoryForm()
        return context

    ...

Upvotes: 17

seuling
seuling

Reputation: 2966

I have a similar situation. I tried many things, and now using ListView with FormMixin.

First, I make FormListView inheriting ListView and FormMixin. Here's my code

from django.http import Http404
from django.views.generic import ListView
from django.views.generic.edit import FormMixin
from django.utils.translation import ugettext as _


class FormListView(FormMixin, ListView):
    def get(self, request, *args, **kwargs):
        # From FormMixin
        form_class = self.get_form_class()
        self.form = self.get_form(form_class)

        # From ListView
        self.object_list = self.get_queryset()
        allow_empty = self.get_allow_empty()
        if not allow_empty and len(self.object_list) == 0:
            raise Http404(_(u"Empty list and '%(class_name)s.allow_empty' is False.")
                          % {'class_name': self.__class__.__name__})

        context = self.get_context_data(object_list=self.object_list, form=self.form)
        return self.render_to_response(context)

    def post(self, request, *args, **kwargs):
        return self.get(request, *args, **kwargs)

As you see, I made my own get for FormMixin andListView` both.

And inheriting this, I make my own SomeFormListView, In your case InventoryFormListView.

class InventoryFormListView(FormListView):
    template_name ='system/inventory_list.html'
    context_object_name = 'inventorys'
    model = models.Inventory

    # FormListView
    form_class = YourCustomModelForm

    # your custom method 

Upvotes: 1

Lemayzeur
Lemayzeur

Reputation: 8525

Send the form through get_context_data() method:

def get_context_data(self, **kwargs):
     context =  super(InventoryListView,self).get_context_data(**kwargs)
     context['form'] = InventoryForm()
     return context

Upvotes: 2

Related Questions