Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django - return each model value without field

Tags:

python

django

The Priority model has three values, for each of them values I'm returning an inlineform which allows the user to set a score for each priority & then save with the Project.

This is what it currently looks like: Current view

My problem is: how can I automatically show all the priority values and allow the user to enter the score but not have to pick the Priority. Is it possible to show it like the image below?

What I'm trying to do.

Views.py

class ProjectCreateview(LoginRequiredMixin, CreateView):
    model = Project
    form_class = ProjectCreationForm
    login_url = "/login/"
    success_url = '/'

    def get_context_data(self, **kwargs):

        PriorityChildFormset = inlineformset_factory(
            Project, ProjectPriority, fields=('project', 'priority', 'score'), can_delete=False, extra=Priority.objects.count(),
        )

        data = super().get_context_data(**kwargs)
        if self.request.POST:
            data['priorities'] = PriorityChildFormset(self.request.POST)
        else:
            data['priorities'] = PriorityChildFormset()
        return data

    def form_valid(self, form):
        context = self.get_context_data()
        prioritycriteria = context["priorities"]
        form.instance.creator = self.request.user
        self.object = form.save()
        prioritycriteria.instance = self.object
        if prioritycriteria.is_valid():
            prioritycriteria.save()
        return HttpResponseRedirect(self.get_success_url())

Models.py

class Priority(models.Model):
    title = models.CharField(verbose_name="Priority Name", max_length=250)
    
    def __str__(self):
        return self.title
    
class Project(models.Model):
    name = models.CharField(verbose_name="Name", max_length=100)
    details = models.TextField(verbose_name="Details/Description", blank=False)
    creator = models.ForeignKey(User, on_delete=models.CASCADE)
    
class ProjectPriority(models.Model):
    project = models.ForeignKey(Project, on_delete=models.CASCADE)
    priority = models.ForeignKey(Priority, on_delete=models.CASCADE)
    score = models.CharField(max_length=1000, choices=priority_choices)
    
    class Meta:
        verbose_name = "Priority"
        verbose_name_plural = "Priorities"

Template

<table class="table table-light">
  <tbody>
  {{ priorities.management_form }}
    {% for priority in priorities.forms %}
    <tr>
      {% for field in priority.visible_fields %}
      <td>
        {{ field.errors.as_ul }}
        {{ field }}
      </td>
      {% endfor %}
    </tr>
    {% endfor %}
  </tbody>
</table>
like image 888
user Avatar asked Apr 20 '21 06:04

user


Video Answer


1 Answers

You can do this by using initial data with your formset (see the Django documentation here).

In your views.py code you can add this line to generate some initial values for the priority fields:

initial = [{'priority': priority} for priority in Priority.objects.all()]

And then pass it to your formset:

data['priorities'] = PriorityChildFormset(initial=initial)

Note that initial expects a list of the same length as the formset you created, which is defined by the extra parameter. This works because both of these parameters have been defined using the same base queryset (i.e. Priority.objects). If a filter were applied - it would need to apply in both places.

Changing how it displays

In addition, if you want to prevent the priority fields from being changed by the user using the dropdown menu, you can pass a widgets keyword argument to inlineformset_factory to set a disabled attribute on the <select> element that gets generated e.g.:

from django import forms
...

widgets={'priority': forms.Select(attrs={'disabled': True})}

If you want the field to show only text - this is more difficult. The string representation of the related object that we want is buried in the field choices that are used to generate each <option>. If you want to do this, you can dig out each field manually:

{% for priority in priorities.forms %}
  <tr>
    <td>
      {{ priority.priority.errors.as_ul }}
      {% for value, name in priority.priority.field.choices %}
        {% if value == priority.priority.value %}
          {{ name }}
        {% endif %}
      {% endfor %}
      {{ priority.priority.as_hidden }}
    </td>
    <td>
      {{ priority.score.errors.as_ul }}
      {{ priority.score }}
    </td>
  </tr>
{% endfor %}
like image 172
elyas Avatar answered Sep 20 '22 12:09

elyas