Aagam Sheth
Aagam Sheth

Reputation: 691

Class Based View: CreateView success_url is not working

I have created a a class based view that inherits CreateView which dynamically generates a form based on the url parameters. I have other Views that also have the same functions but they do redirect to the ListView but this CBV gives 404 error with request url : http://127.0.0.1:8000/create/StudentPerformance/tblstandard/ (StudentPerformance is app_label, tblstandard is model_label) which is the url for below CreateView. And I am getting an Attribute Error: Generic detail view ModelObjectCreateView must be called with either an object pk or a slug in the URLconf.

CreateView

Only showing create view but ModelObjectUpadteView, ModelObjectDeleteView that work are of the same structure and they work.

class ModelObjectCreateView(SuccessMessageMixin, PermissionRequiredMixin, LoginRequiredMixin, CreateView):
    context_object_name = 'object'
    app_label = ""
    model_label = ""
    fields_label = ""
    template_label = ""
    success_label = ""
    success_message = f"%(object)s successfully created!!!"

    def dispatch(self, request, *args, **kwargs):
        self.app_label = kwargs.get('app_label', None)
        self.model_label = kwargs.get('model_label', None)
        self.model = apps.get_model(self.app_label, self.model_label.capitalize())
        self.get_modelobject_fields_label()

        self.template_label = kwargs.get('template_label', None)
        self.template_name = self.get_template_name_label()

# ----------------------------------
# success_url is assigned
# ----------------------------------
        self.success_url = self.get_success_url_label()

        self.permission_required = self.get_permissions_required_label()
        try:
            ret = super(ModelObjectCreateView, self).dispatch(request, *args, **kwargs)
        except AttributeError as e:
            print(e)
            raise Http404
        return ret

    def form_valid(self, form):
        form.instance.created_by = self.request.user
        return super().form_valid(form)

    def get_template_name_label(self):
        if self.template_label == '0':
            template_name = f'{self.app_label}/{self.model_label}_create.html'
        elif self.template_label:
            path = self.template_label.split("-")
            file = ""
            for folder in path:
                file += folder + "/"
            template_name = f'{file[:-1]}.html'
        else:
            template_name = 'modelobject_create.html'
        return template_name

    def get_modelobject_fields_label(self, form1=None):
        if not form1 is None:
            self.form_class = form1
        else:
            model_label = list()
            field_label = list()
            for app in apps.get_app_configs():
                for model in app.get_models():
                    model_label.append(model._meta.verbose_name.replace(" ", ""))
            field_labels = self.model._meta.get_fields()
            for field in field_labels:
                if field.name == f'{self.model.objects.model._meta.db_table}_id':
                    pass
                elif field.name in model_label:
                    pass
                elif not field.editable:
                    pass
                else:
                    field_label.append(field.name)
            self.fields = field_label
        return 0

# -------------------------------------------------------------
# This creates the success url that is assigned to self.success_url in dispatch() method.
# -------------------------------------------------------------
    def get_success_url_label(self):
        if self.success_label == "0":
            success_url = 'modelobject_success_url.html'
        elif self.success_label == "1":
            success_url = f'{self.app_label}/{self.model_label}_success_url.html'
        elif self.success_label:
            success_url = self.success_label
        else:
            success_url = reverse('MySchoolHome:modelobject_list_view',
                                  kwargs={'app_label': self.app_label, 'model_label': self.model_label})
        return success_url

    def get_success_message(self, cleaned_data):
        return self.success_message % dict(
            cleaned_data,
            object=self.get_object(),
        )

    def get_permissions_required_label(self, permission=None, permission_type=None):
        if permission is not None:
            permission_required_label = permission
        else:
            if permission_type is None:
                permission_required_label = f'{self.app_label}.add_{self.model_label}'
            else:
                return HttpResponse(status=403)
        return permission_required_label

Urls.py (excerpt)

app_name = 'MySchoolHome'

urlpatterns += [
    path('list/<str:app_label>/<str:model_label>/',
         views.MshModelListView.as_view(extra_context={'page_context': {'titleTag': '1'}}),
         name='modelobject_list_view'),
    path('list/<str:app_label>/<str:model_label>/<str:template_label>/',
         views.MshModelListView.as_view(extra_context={'page_context': {'titleTag': '1'}}),
         name='modelobject_list_view'),

    path('create/<str:app_label>/<str:model_label>/',
         generic.ModelObjectCreateView.as_view(extra_context={'page_context': {'titleTag': '1'}}),
         name='modelobject_create_view'),
    path('create/<str:app_label>/<str:model_label>/<int:pk>/',
         generic.ModelObjectCreateView.as_view(extra_context={'page_context': {'titleTag': '1'}}),
         name='modelobject_create_view'),
    path('create/<str:app_label>/<str:model_label>/<str:template_label>/',
         generic.ModelObjectCreateView.as_view(), name='modelobject_create_view'),

I have seen other SO posts for the error but and I tried them too but they are not helping. I also though of giving a fixed url to see if success_url even works but it dosent.

Edit : Showing Both generic.py (my extened generic views) file and the urls.py for further reference : generic.py

from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.contrib.messages.views import SuccessMessageMixin
from django.http import Http404, HttpResponse
from django.apps import apps
from django.template.exceptions import TemplateDoesNotExist
from django.urls import reverse_lazy, reverse
from django.views.generic import ListView, CreateView, DetailView, UpdateView, DeleteView
from django.contrib.auth import views as auth_views


class ModelObjectListView(PermissionRequiredMixin, LoginRequiredMixin, ListView):
    context_object_name = 'object'
    app_label = ""
    model_label = ""
    template_label = ""
    paginate_by = 25

    def dispatch(self, request, *args, **kwargs):
        self.app_label = kwargs.get('app_label', None)
        self.model_label = kwargs.get('model_label', None)
        self.model = apps.get_model(self.app_label, self.model_label.capitalize())

        self.template_label = kwargs.get('template_label', None)
        self.template_name = self.get_template_name_label()
        self.permission_required = self.get_permissions_required_label()
        try:
            ret = super(ModelObjectListView, self).dispatch(request, *args, **kwargs)
        except AttributeError:
            raise Http404
        return ret

    def get_queryset(self):
        filters = {}
        for key, value in self.request.GET.items():
            if key != "page":
                if value != '':
                    filters[f'{key}__icontains'] = value
        filter_list = self.model.objects.filter(**filters)
        return filter_list

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['fields'] = [
            field for field in self.get_modelobject_fields_label()
        ]
        context['display_fields'] = [
            field.replace('_', ' ').title() for field in self.get_modelobject_fields_label()
        ]
        return context

    def get_modelobject_fields_label(self, form1=None):
        model_label = list()
        field_label = list()
        display_field_label = list()
        for app in apps.get_app_configs():
            for model in app.get_models():
                model_label.append(model._meta.verbose_name.replace(" ", ""))
        field_labels = self.model._meta.get_fields()
        for field in field_labels:
            if field.name == f'{self.model.objects.model._meta.db_table}_id':
                pass
            elif field.name in model_label:
                pass
            # elif not field.editable:
            #     pass
            else:
                field_label.append(field.name)
        return field_label

    def get_template_name_label(self):
        if self.template_label == '0':
            template_name = f'{self.app_label}/{self.model_label}_create.html'
        elif self.template_label:
            path = self.template_label.split("-")
            file = ""
            for folder in path:
                file += folder + "/"
            template_name = f'{file[:-1]}.html'
        else:
            template_name = 'modelobject_list.html'
        return template_name

    def get_permissions_required_label(self, permission=None, permission_type=None):
        if permission is not None:
            permission_required_label = permission
        else:
            if permission_type is None:
                permission_required_label = f'{self.app_label}.view_{self.model_label.lower()}'
            else:
                return HttpResponse(status=403)
        return permission_required_label


class ModelObjectCreateView(SuccessMessageMixin, PermissionRequiredMixin, LoginRequiredMixin, CreateView):
    context_object_name = 'object'
    app_label = ""
    model_label = ""
    fields_label = ""
    template_label = ""
    success_label = ""
    success_message = f"%(object)s successfully created!!!"

    def dispatch(self, request, *args, **kwargs):
        self.app_label = kwargs.get('app_label', None)
        self.model_label = kwargs.get('model_label', None)
        self.model = apps.get_model(self.app_label, self.model_label.capitalize())
        self.get_modelobject_fields_label()

        self.template_label = kwargs.get('template_label', None)
        self.template_name = self.get_template_name_label()

        self.success_url = self.get_success_url_label()

        self.permission_required = self.get_permissions_required_label()
        try:
            ret = super(ModelObjectCreateView, self).dispatch(request, *args, **kwargs)
        except AttributeError as e:
            print(e)
            raise Http404
        return ret

    def form_valid(self, form):
        form.instance.created_by = self.request.user
        return super().form_valid(form)

    def get_template_name_label(self):
        if self.template_label == '0':
            template_name = f'{self.app_label}/{self.model_label}_create.html'
        elif self.template_label:
            path = self.template_label.split("-")
            file = ""
            for folder in path:
                file += folder + "/"
            template_name = f'{file[:-1]}.html'
        else:
            template_name = 'modelobject_create.html'
        return template_name

    def get_modelobject_fields_label(self, form1=None):
        if not form1 is None:
            self.form_class = form1
        else:
            model_label = list()
            field_label = list()
            for app in apps.get_app_configs():
                for model in app.get_models():
                    model_label.append(model._meta.verbose_name.replace(" ", ""))
            field_labels = self.model._meta.get_fields()
            for field in field_labels:
                if field.name == f'{self.model.objects.model._meta.db_table}_id':
                    pass
                elif field.name in model_label:
                    pass
                elif not field.editable:
                    pass
                else:
                    field_label.append(field.name)
            self.fields = field_label
        return 0

    def get_success_url_label(self):
        if self.success_label == "0":
            success_url_label = 'modelobject_success_url.html'
        elif self.success_label == "1":
            success_url_label = f'{self.app_label}/{self.model_label}_success_url.html'
        elif self.success_label:
            success_url_label = self.success_label
        else:
            success_url_label = reverse('MySchoolHome:modelobject_list_view',
                                  kwargs={'app_label': self.app_label, 'model_label': self.model_label})
        return success_url_label

    def get_success_message(self, cleaned_data):
        return self.success_message % dict(
            cleaned_data,
            object=self.get_object(),
        )

    def get_permissions_required_label(self, permission=None, permission_type=None):
        if permission is not None:
            permission_required_label = permission
        else:
            if permission_type is None:
                permission_required_label = f'{self.app_label}.add_{self.model_label}'
            else:
                return HttpResponse(status=403)
        return permission_required_label


class ModelObjectUpdateView(SuccessMessageMixin, PermissionRequiredMixin, LoginRequiredMixin, UpdateView):
    context_object_name = 'object'
    app_label = ""
    model_label = ""
    fields_label = ""
    template_label = ""
    success_label = ""
    success_message = f"%(object)s successfully updated!!!"

    def dispatch(self, request, *args, **kwargs):
        self.app_label = kwargs.get('app_label', None)
        self.model_label = kwargs.get('model_label', None)
        self.model = apps.get_model(self.app_label, self.model_label.capitalize())
        self.get_modelobject_fields_label()

        self.template_label = kwargs.get('template_label', None)
        self.template_name = self.get_template_name_label()

        self.success_url = self.get_success_url_label()

        self.permission_required = self.get_permissions_required_label()
        print(self.get_permissions_required_label())
        try:
            ret = super(ModelObjectUpdateView, self).dispatch(request, *args, **kwargs)
        except AttributeError:
            raise Http404
        return ret

    def form_valid(self, form):
        form.instance.created_by = self.request.user
        return super().form_valid(form)

    def get_template_name_label(self):
        if self.template_label == '0':
            template_name = f'{self.app_label}/{self.model_label}_create.html'
        elif self.template_label:
            path = self.template_label.split("-")
            file = ""
            for folder in path:
                file += folder + "/"
            template_name = f'{file[:-1]}.html'
        else:
            template_name = 'modelobject_create.html'
        return template_name

    def get_modelobject_fields_label(self, form1=None):
        if not form1 is None:
            self.form_class = form1
        else:
            model_label = list()
            field_label = list()
            for app in apps.get_app_configs():
                for model in app.get_models():
                    model_label.append(model._meta.verbose_name.replace(" ", ""))
            field_labels = self.model._meta.get_fields()
            for field in field_labels:
                if field.name == f'{self.model.objects.model._meta.db_table}_id':
                    pass
                elif field.name in model_label:
                    pass
                elif not field.editable:
                    pass
                else:
                    field_label.append(field.name)
            self.fields = field_label
        return 0

    def get_success_url_label(self):
        if self.success_label == "0":
            success_url = 'modelobject_success_url.html'
        elif self.success_label == "1":
            success_url = f'{self.app_label}/{self.model_label}_success_url.html'
        elif self.success_label:
            success_url = self.success_label
        else:
            success_url = reverse('MySchoolHome:modelobject_list_view',
                                  kwargs={'app_label': self.app_label, 'model_label': self.model_label})
        return success_url

    def get_success_message(self, cleaned_data):
        return self.success_message % dict(
            cleaned_data,
            object=self.get_object(),
        )

    def get_permissions_required_label(self, permission=None, permission_type=None):
        if permission is not None:
            permission_required_label = permission
        else:
            if permission_type is None:
                permission_required_label = f'{self.app_label}.change_{self.model_label.lower()}'
            else:
                return HttpResponse(status=403)
        return permission_required_label


class ModelObjectDeleteView(PermissionRequiredMixin, LoginRequiredMixin, DeleteView):
    context_object_name = f'object'
    app_label = ""
    model_label = ""
    template_label = ""
    success_label = ""

    def dispatch(self, request, *args, **kwargs):
        self.model_label = kwargs.get('model_label', None)
        self.app_label = kwargs.get('app_label', None)
        self.model = apps.get_model(self.app_label, self.model_label.capitalize())

        self.template_label = kwargs.get('template_label', None)
        self.template_name = self.get_template_name_label()

        self.success_label = kwargs.get('success_label', None)
        self.success_url = self.get_success_url_label()

        self.permission_required = self.get_permissions_required_label()
        try:
            ret = super(ModelObjectDeleteView, self).dispatch(request, *args, **kwargs)
        except AttributeError:
            raise Http404
        except TemplateDoesNotExist:
            return HttpResponse(status=501)
        return ret

    def get_template_name_label(self):
        if self.template_label == "0":
            template_name = f'{self.app_label}/{self.model_label}_delete.html'
        elif self.template_label:
            path = self.template_label.split("-")
            file = ""
            for folder in path:
                file += folder + "/"
            template_name = f'{file[:-1]}.html'
        else:
            template_name = 'modelobject_confirm_delete.html'
        return template_name

    def get_success_url_label(self):
        if self.success_label == "0":
            success_url = f'{self.app_label}/{self.model_label}_success_url.html'
        elif self.success_label:
            success_url = reverse('MySchoolHome:modelobject_list_view',
                                  kwargs={'app_label': self.app_label, 'model_label': self.model_label,
                                          'template_label': self.success_label})
        else:
            success_url = reverse('MySchoolHome:modelobject_list_view',
                                  kwargs={'app_label': self.app_label, 'model_label': self.model_label})
        return success_url

    def get_permissions_required_label(self, permission=None, permission_type=None):
        if permission is not None:
            permission_required_label = permission
        else:
            if permission_type is None:
                permission_required_label = f'{self.app_label}.delete_{self.model_label}'
            else:
                return HttpResponse(status=403)
        return permission_required_label

    def delete(self, request, *args, **kwargs):
        obj = self.get_object()
        messages.success(self.request, f"{obj} successfully deleted!!!")
        return super(ModelObjectDeleteView, self).delete(request, *args, **kwargs)

urls.py

from django.urls import path
from django.contrib.auth import views as auth_views
from django.http import request
from . import views
from aagam_packages.django.view_extensions import generic

app_name = 'MySchoolHome'

urlpatterns = [
    path('sitemap/', views.sitemap, name='sitemap'),
]

urlpatterns += [
    path('login/', auth_views.LoginView.as_view(extra_context={'page_context': page_context, 'titleTag': 'Login'}),
         name='login'),
    path('logout/', auth_views.LogoutView.as_view(extra_context={'page_context': page_context, 'titleTag': 'Logout'}),
         name='logout'),
    path('change-password/', auth_views.PasswordChangeView.as_view(), name="change_password"),
    path('settings/', auth_views.LogoutView.as_view(extra_context={'page_context': page_context, 'titleTag': 'Logout'}),
         name='settings'),
    path('documentation/',
         auth_views.LogoutView.as_view(extra_context={'page_context': page_context, 'titleTag': 'Logout'}),
         name='documentation'),
]

urlpatterns += [
    path('list/<str:app_label>/<str:model_label>/',
         generic.ModelObjectListView.as_view(extra_context={'page_context': {'titleTag': '1'}}),
         name='modelobject_list_view'),
    path('list/<str:app_label>/<str:model_label>/<str:template_label>/',
         generic.ModelObjectListView.as_view(extra_context={'page_context': {'titleTag': '1'}}),
         name='modelobject_list_view'),

    path('create/<str:app_label>/<str:model_label>/',
         generic.ModelObjectCreateView.as_view(extra_context={'page_context': {'titleTag': '1'}}),
         name='modelobject_create_view'),
    path('create/<str:app_label>/<str:model_label>/<str:template_label>/',
         generic.ModelObjectCreateView.as_view(), name='modelobject_create_view'),

    path('update/<str:app_label>/<str:model_label>/<int:pk>/',
         views.MshModelUpdateView.as_view(extra_context={'page_context': {'titleTag': '1'}}),
         name='modelobject_update_view'),
    path('update/<str:app_label>/<str:model_label>/<int:pk>/<str:template_label>/',
         views.MshModelUpdateView.as_view(extra_context={'page_context': {'titleTag': '1'}}),
         name='modelobject_update_view'),

    path('delete/<str:app_label>/<str:model_label>/<int:pk>/',
         generic.ModelObjectDeleteView.as_view(), name='modelobject_delete_view'),
    path('delete/<str:app_label>/<str:model_label>/<int:pk>/<str:template_label>/',
         generic.ModelObjectDeleteView.as_view(), name='modelobject_delete_view'),
    path('delete/<str:app_label>/<str:model_label>/<int:pk>/<str:template_label>/<str:success_label>/',
         generic.ModelObjectDeleteView.as_view(), name='modelobject_delete_view'),
]

urlpatterns += [
    path('', views.home, name='home'),
    path('student/', views.student_dashboard, name='student_dashboard'),
    path('educator/', views.educator_dashboard, name='educator_dashboard'),
    path('principal/', views.principal_dashboard, name='principal_dashboard'),
    path('staff/', views.staff_dashboard, name='staff_dashboard'),
]

Upvotes: 1

Views: 354

Answers (1)

Abdul Aziz Barkat
Abdul Aziz Barkat

Reputation: 21812

In your get_success_message method you call get_object which tries to get the object using the kwargs passed to the view. Instead you should use the instance attribute object that is set by form_valid after creating the object:

def get_success_message(self, cleaned_data):
    return self.success_message % dict(
        cleaned_data,
        object=self.object,
    )

Note: You appear to have lot's of code repeating in your classes, consider making a mixin to prevent this.

Upvotes: 1

Related Questions