Search code examples
pythondjangodjango-modelsdjango-formsdjango-generic-views

Django filter generic form Select attribute from generic class-based view by Username or prepopulate and hide the form Select-attribute


I Need to Restrict the Options in a Select Field of a Django Form for not staff users.

So these are my models.py:

#!/usr/bin/python3
from django.db import models
from django.urls import reverse


class Extension(models.Model):
    username = models.CharField(primary_key=True, max_length=200, help_text='')
    callerid = models.CharField(max_length=200, help_text='')
    extension = models.CharField(max_length=3, help_text='')
    firstname = models.CharField(max_length=200, help_text='')
    lastname = models.CharField(max_length=200, help_text='')
    password = models.CharField(max_length=200, help_text='')
    context = models.ForeignKey('Context', on_delete=models.SET_NULL, null=True)

    def get_absolute_url(self):
        return reverse('extension-detail', args=[str(self.username)])

    def my_get_absolute_url(self):
        return reverse('my-extension-detail', args=[str(self.username)])

    def __str__(self):
        return self.username


class Context(models.Model):
    name = models.CharField(primary_key=True, max_length=200, help_text='')
    countryprefix = models.CharField(max_length=200, help_text='')
    cityprefix = models.CharField(max_length=200, help_text='')
    number = models.CharField(max_length=200, help_text='')
    extensionsfrom = models.CharField(max_length=200, help_text='')
    extensionstill = models.CharField(max_length=200, help_text='')
    portscount = models.CharField(max_length=200, help_text='')

    def get_absolute_url(self):
        return reverse('context-detail', args=[str(self.name)])

    def my_get_absolute_url(self):
        return reverse('my-context-detail', args=[str(self.name)])

    def __str__(self):
        return self.name

views.py:

#!/usr/bin/python3
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.contrib.auth.models import Permission
from catalog.models import Extension, Context
from django.shortcuts import render
from django.urls import reverse_lazy
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView


class ExtensionCreate(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
    model = Extension
    fields = '__all__'
    permission_required = 'catalog.add_extension'


class ExtensionUpdate(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
    model = Extension
    fields = '__all__'
    permission_required = 'catalog.change_extension'


class ExtensionDelete(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
    model = Extension
    success_url = reverse_lazy('extensions')
    permission_required = 'catalog.delete_extension'

urls.py:

#!/usr/bin/python3
from . import views
from django.urls import path


urlpatterns = [
    path('', views.index, name='index'),
    path('extensions/', views.ExtensionListView.as_view(), name='extensions'),
    path('extension/<str:pk>', views.ExtensionDetailView.as_view(), name='extension-detail'),
    path('extension/create/', views.ExtensionCreate.as_view(), name='extension-create'),
    path('extension/<str:pk>/update/', views.ExtensionUpdate.as_view(), name='extension-update'),
    path('extension/<str:pk>/delete/', views.ExtensionDelete.as_view(), name='extension-delete'),
    path('myextensions/', views.ExtensionsByUserListView.as_view(), name='my-extensions'),
    path('myextension/<str:pk>', views.ExtensionsByUserDetailView.as_view(), name='my-extension-detail'),
    path('contexts/', views.ContextListView.as_view(), name='contexts'),
    path('context/<str:pk>', views.ContextDetailView.as_view(), name='context-detail'),
    path('context/create/', views.ContextCreate.as_view(), name='context-create'),
    path('context/<str:pk>/update/', views.ContextUpdate.as_view(), name='context-update'),
    path('context/<str:pk>/delete/', views.ContextDelete.as_view(), name='context-delete'),
    path('mycontexts/', views.ContextByUserListView.as_view(), name='my-contexts'),
    path('mycontext/<str:pk>', views.ContextByUserDetailView.as_view(), name='my-context-detail'),
]

template:

{% extends "base_generic.html" %}

{% block content %}
  <form action="" method="post">
    {% csrf_token %}
    <table>
    {{ form.as_table }}
    </table>
    <input type="submit" value="Submit">
  </form>
{% endblock %}

In this is how it looks like: enter image description here

The Username is always the same as one of the contexts.
Only Staff User can create a new Context and new Users.
The users then should add their extensions.

While employees should be able to select the context when creating a new extension, customers should only be able to see or select their context in the list.

Therefore it is needed to filter the Select-attribute for non-staff members so that only the user's context is visible, which is equal to his Username.

Alternatively, I want to prepopulate and hide the context form field with the Username (=own context)

How can I do either of this in the easiest way possible?

Here is what I tried myself so far:

Like described in this how-to: https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Forms#Renew-book_form_using_a_Form_and_function_view I Defined a Form in forms.py:

#!/usr/bin/python3
from django import forms
class MyExtensionCreateForm(forms.Form):
    # username = forms.CharField(help_text="")
    # callerid = forms.CharField(help_text="")
    # context = forms.CharField(help_text="")

    firstname = forms.CharField(help_text="")
    lastname = forms.CharField(help_text="")
    extension = forms.CharField(help_text="")
    password = forms.CharField(help_text="")

    def clean_data(self):
        data = self.cleaned_data
        # Remember to always return the cleaned data.
        return data

I then added the folowing to the views.py:

from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.urls import reverse
from catalog.forms import MyExtensionCreateForm

def MyExtensionCreate(request):
    # extension = get_object_or_404(Extension)
    # If this is a POST request then process the Form data
    if request.method == 'POST':
        # Create a form instance and populate it with data from the request (binding):
        form = MyExtensionCreateForm(request.POST)
        # Check if the form is valid:
        if form.is_valid():
            # process the data in form.cleaned_data as required
            form.firstname = form.cleaned_data['firstname']
            form.lastname = form.cleaned_data['lastname']
            form.extension = form.cleaned_data['extension']
            form.password = form.cleaned_data['password']
            # Prepopulated Fields
            form.context = request.user
            form.callerid = str(form.cleaned_data['firstname'])+" "+str(form.cleaned_data['lastname'])+" "+str(form.cleaned_data['extension'])
            form.username = str(request.user)+"_"+str(form.cleaned_data['extension'])
            form.save()
            # redirect to a new URL:
            return HttpResponseRedirect(reverse('my-extensions'))
    # If this is a GET (or any other method) create the default form.
    else:
        form = MyExtensionCreateForm({'context': request.user})
    context = {
        'form': form,
    }
    return render(request, 'catalog/extension_form-by-user.html', context)

# class MyExtensionCreate(LoginRequiredMixin, CreateView):
#     model = Extension
#     fields = '__all__'
#     form_class = MyExtensionCreateForm


Then I added a new URL to URL patterns in urls.py and added the new Link to the base_generic.html

path('myextensions/create/', views.MyExtensionCreate, name='my-extension-create'),
<li><a href="{% url 'my-extension-create' %}">Add Extension</a></li>

I can view the form, and if I add the context field to the visible form fields, I can see that the context will initially fill with the logged-in username (See First Picture Below). But as soon as I submit the form I'll get Error's no matter what I try, So basically the GET is working, but the POST isn't really.

See Folowing Pictures:

enter image description here enter image description here enter image description here


Solution

  • I was able to Filter the Choices by the Username in the ModelForm but since then I'm not able to save the form to the database anymore. Here is the new Stack Post with the Resolution of this Post here and the new Question/Problem. Django: saving the Form does not work (The ModelForm Filters the ForeignKey choices by request.user)