No URL to redirect to. Either provide a url or define a get_absolute_url method on the Model

19,758

Solution 1

The best way to do this is to add a method get_success_url on the create view and use that to redirect back to the detail view. In the create view you have the object after it is saved, like so

class LawyerReviewCreate(CreateView):
    def get_success_url(self):
        return reverse('lawyer_detail', kwargs={'lawyer_slug': self.object.lawyer_slug})

This will then automatically send the user back to the detail view if the form is valid.

Also, make sure your kwargs is using the correct key, it would appear that you are using review_slug in some cases and lawyer_slug in other cases

Solution 2

We can follow Django’s suggestion and add a "get_absolute_url" to our model. It sets a canonical URL for an object so even if the structure of your URLs changes in the future, the reference to the specific object is the same. In short, you should add a get_absolute_url() method to each model you write.

def get_absolute_url(self): # new
    return reverse('lawyer_detail', args=[str(self.id)])

This should solve your problem

Share:
19,758

Related videos on Youtube

user2901792
Author by

user2901792

Updated on September 16, 2022

Comments

  • user2901792
    user2901792 over 1 year

    I am using CreateView to build a form. The CreateView is called from the DetailView. Once the form is submitted, I want for the validated, submitted data to be returned back to the initial DetailView.

    The DetailView calls up the CreateView just fine. The form works as expected until it is submitted. Then, I get this error: No URL to redirect to. Either provide a url or define a get_absolute_url method on the Model.

    I tried using this solution, but it kicks out the same error. I tried passing the data and redirecting through the URL that calls the CreateView. I still get the same error.

    Can someone please tell me how to redirect the CreateView back to the original DetailView (and passing back the validated data?

    models.py

    class Lawyer(models.Model):
        name = models.CharField(max_length=100, default='')
        practice_area = models.CharField(max_length=100, default='')
        address = models.CharField(max_length=255, default='')
        city = models.CharField(max_length=50, default='')
        state = models.CharField(max_length=2, default='')
        zipcode = models.CharField(max_length=10, default='')
        telephone = models.CharField(max_length=15, default='')
        years_practice = models.IntegerField(default=10)
        objects = models.Manager()
        lawyer_slug = models.SlugField(default='')
    
        def get_absolute_url(self):
            return reverse('lawyer_detail', kwargs={'lawyer_slug': self.lawyer_slug})
    
        def __str__(self):
            return self.name
    
    class Review(models.Model):
        RATING_CHOICES = (
            (0, '0'),
            (1, '1'),
            (2, '2'),
            (3, '3'),
            (4, '4'),
            (5, '5'),
        )
        lawyer = models.ForeignKey(Lawyer, null=True)
        review_created = models.DateTimeField('Date of Review', auto_now_add=True)
        reviewer_name = models.CharField(max_length=55, default='')
        reviewer_city = models.CharField(max_length=55, default='')
        reviewer_state = models.CharField(max_length=2, default='')
        email = models.EmailField(default='')
        rating = models.IntegerField(default=1, choices=RATING_CHOICES)
        review_comment = models.TextField(default='')
        review_slug = models.SlugField(default='')
    
        def get_absolute_url(self):
            return reverse('lawyer_createreview', kwargs={'review_slug': self.review_slug})
    
        def __str__(self):
            return self.review_slug
    

    views.py

    class LawyerDetail(DetailView):
        model = Lawyer
    
        template = 'lawyer_detail.html'
    
        context_object_name = 'lawyer'
    
        def get_object(self):
            lawyer_slug = Lawyer.objects.get(
                lawyer_slug=self.kwargs.get('lawyer_slug')
            )
            return lawyer_slug
    
        def get_context_data(self, **kwargs):
            context = super(LawyerDetail, self).get_context_data(**kwargs)
            context['lawyer_reviews'] = self.object.review_set.all()
            return context
    
    class LawyerReviewCreate(CreateView):
        model = Review
        form_class = ReviewForm
    
        def get_form_kwargs(self, **kwargs):
            kwargs = super(LawyerReviewCreate, self).get_form_kwargs()
            redirect = self.request.GET.get('next')
            if redirect:
                if 'initial' in kwargs.keys():
                    kwargs['initial'].update({'next': redirect})
                else:
                    kwargs['initial'] = {'next': redirect}
            return kwargs
    
        def form_invalid(self, form):
            import pdb;pdb.set_trace()  # debug example
    
            return super(LawyerReviewCreate, self).form_invalid(form)
    
        def form_valid(self, form):
            redirect = form.cleaned_data.get('next')
            if redirect:
                self.success_url = redirect
            return super(LawyerReviewCreate, self).form_valid(form)
    

    urls.py

    url(r'^lawyers/(?P<lawyer_slug>[\w-]+)/$', LawyerDetail.as_view(), name='lawyer_detail'),
    url(r'^lawyers/(?P<lawyer_slug>[\w-]+)/createreview/$', LawyerReviewCreate.as_view(), name='lawyer_createreview'),
    

    template.html (calls CreateView and part that displays the returned data)

    <div class="review_buttom_wrapper">
        <a href="{% url 'lawyer_createreview' lawyer.lawyer_slug %}?next={% url 'lawyer_detail' lawyer.lawyer_slug %}">
            <button class="review_button">
                <strong>Review</strong> {{ lawyer.name }}
            </button>
        </a>
    </div>
    
    {% for review in lawyer_reviews %}
    <div style="padding-left: 15px; padding-right: 15px; overflow:auto;">
        <div class="review-masthead">
            <div class="medium-3 columns">
                <p class="posttime">{{ review.review_created|timesince }} ago </p>
                <p class="review-title">{{ review.user_name }} <span class="location">{{ review.lawyer.city }}, {{ review.lawyer.state }}</span></p> 
            </div>
            <div class="medium-7 columns">
                <p>{{ review.review_comment }}</p>
            </div>
            <div class="medium-2 columns">
                <div class="user_rating">
                    Rating
                </div>
                <div class="rating_number">
                    {{ review.rating }}
                </div>
    
            </div>
        </div>
    {% endfor %}
    

    forms.py

    RATING_CHOICES = (
            (1, '1'),
            (2, '2'),
            (3, '3'),
            (4, '4'),
            (5, '5'),
        )
    
    class ReviewForm(forms.ModelForm):
        reviewer_name = forms.CharField(widget = forms.TextInput(attrs={'class': 'review_input_box', 'placeholder': 'Your name'}))
        reviewer_city = forms.CharField(widget = forms.TextInput(attrs={'class': 'review_input_box', 'placeholder': 'Your city'}))
        reviewer_state = forms.CharField(widget = forms.TextInput(attrs={'class': 'review_input_box', 'placeholder': 'Your state'}))
        rating = forms.ChoiceField(choices = RATING_CHOICES, label="", initial='', widget = forms.Select(attrs={'class': 'review_selector'}), required=True)
        email = forms.EmailField(widget = forms.TextInput(attrs={'class': 'review_input_box', 'placeholder': '[email protected]'}))
        review_comment = forms.CharField(widget = forms.Textarea(attrs={'class': 'review_input_box', 'placeholder': 'What would you like to say?'}))
    
        class Meta:
            model = Review
            fields = ['reviewer_name', 'reviewer_city', 'reviewer_state', 'rating', 'email', 'review_comment']
    
    
    Traceback:
    
    File "/xxxx/xxxxxxxxxx/xxxx/xxxxxxxxxxxx/lib/python3.5/site-packages/django/views/generic/edit.py" in get_success_url
      190.                 url = self.object.get_absolute_url()
    
    File "/xxxx/xxxxxxxxxx/xxxx/xxxxxxxxxxxx/ralph/fathers/models.py" in get_absolute_url
      142.      return reverse('lawyer_createreview', kwargs={'lawyer_slug': self.lawyer_slug})
    
    During handling of the above exception ('Review' object has no attribute 'lawyer_slug'), another exception occurred:
    
    File "/xxxx/xxxxxxxxxx/xxxx/xxxxxxxxxxxx/lib/python3.5/site-packages/django/core/handlers/base.py" in get_response
      149.                     response = self.process_exception_by_middleware(e, request)
    
    File "/xxxx/xxxxxxxxxx/xxxx/xxxxxxxxxxxx/lib/python3.5/site-packages/django/core/handlers/base.py" in get_response
      147.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
    
    File "/xxxx/xxxxxxxxxx/xxxx/xxxxxxxxxxxx/lib/python3.5/site-packages/django/views/generic/base.py" in view
      68.             return self.dispatch(request, *args, **kwargs)
    
    File "/xxxx/xxxxxxxxxx/xxxx/xxxxxxxxxxxx/lib/python3.5/site-packages/django/views/generic/base.py" in dispatch
      88.         return handler(request, *args, **kwargs)
    
    File "/xxxx/xxxxxxxxxx/xxxx/xxxxxxxxxxxx/lib/python3.5/site-packages/django/views/generic/edit.py" in post
      256.         return super(BaseCreateView, self).post(request, *args, **kwargs)
    
    File "/xxxx/xxxxxxxxxx/xxxx/xxxxxxxxxxxx/lib/python3.5/site-packages/django/views/generic/edit.py" in post
      222.             return self.form_valid(form)
    
    File "/xxxx/xxxxxxxxxx/xxxx/xxxxxxxxxxxx/ralph/fathers/views.py" in form_valid
      165.      return super(LawyerReviewCreate, self).form_valid(form)
    
    File "/xxxx/xxxxxxxxxx/xxxx/xxxxxxxxxxxx/lib/python3.5/site-packages/django/views/generic/edit.py" in form_valid
      202.         return super(ModelFormMixin, self).form_valid(form)
    
    File "/xxxx/xxxxxxxxxx/xxxx/xxxxxxxxxxxx/lib/python3.5/site-packages/django/views/generic/edit.py" in form_valid
      108.         return HttpResponseRedirect(self.get_success_url())
    
    File "/xxxx/xxxxxxxxxx/xxxx/xxxxxxxxxxxx/lib/python3.5/site-packages/django/views/generic/edit.py" in get_success_url
      193.                     "No URL to redirect to.  Either provide a url or define"
    
    Exception Type: ImproperlyConfigured at /xxxxxxx/xxxxxxx/xxxxxxx-xxxxxx/createreview/
    Exception Value: No URL to redirect to.  Either provide a url or define a get_absolute_url method on the Model.
    

    Traceback after changing get_absolute_url on Review model

    Traceback:
    
    File "/xxxx/xxxxxxxxxx/xxxx/xxxxxxxxxxxx/lib/python3.5/site-packages/django/core/handlers/base.py" in get_response
      149.                     response = self.process_exception_by_middleware(e, request)
    
    File "/xxxx/xxxxxxxxxx/xxxx/xxxxxxxxxxxx/lib/python3.5/site-packages/django/core/handlers/base.py" in get_response
      147.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
    
    File "/xxxx/xxxxxxxxxx/xxxx/xxxxxxxxxxxx/lib/python3.5/site-packages/django/contrib/admin/sites.py" in wrapper
      265.                 return self.admin_view(view, cacheable)(*args, **kwargs)
    
    File "/xxxx/xxxxxxxxxx/xxxx/xxxxxxxxxxxx/lib/python3.5/site-packages/django/utils/decorators.py" in _wrapped_view
      149.                     response = view_func(request, *args, **kwargs)
    
    File "/xxxx/xxxxxxxxxx/xxxx/xxxxxxxxxxxx/lib/python3.5/site-packages/django/views/decorators/cache.py" in _wrapped_view_func
      57.         response = view_func(request, *args, **kwargs)
    
    File "/xxxx/xxxxxxxxxx/xxxx/xxxxxxxxxxxx/lib/python3.5/site-packages/django/contrib/admin/sites.py" in inner
      244.             return view(request, *args, **kwargs)
    
    File "/xxxx/xxxxxxxxxx/xxxx/xxxxxxxxxxxx/lib/python3.5/site-packages/django/contrib/contenttypes/views.py" in shortcut
      31.     absurl = get_absolute_url()
    
    File "/xxxx/xxxxxxxxxx/xxxx/xxxxxxxxxxxx/xxxxx/xxxxxxx/models.py" in get_absolute_url
      142.      return reverse('lawyer_createreview', kwargs={'review_slug': self.review_slug})
    
    File "/xxxx/xxxxxxxxxx/xxxx/xxxxxxxxxxxx/lib/python3.5/site-packages/django/core/urlresolvers.py" in reverse
      600.     return force_text(iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs)))
    
    File "/xxxx/xxxxxxxxxx/xxxx/xxxxxxxxxxxx/lib/python3.5/site-packages/django/core/urlresolvers.py" in _reverse_with_prefix
      508.                              (lookup_view_s, args, kwargs, len(patterns), patterns))
    
    Exception Type: NoReverseMatch at /admin/r/14/1/
    Exception Value: Reverse for 'lawyer_createreview' with arguments '()' and keyword arguments '{'review_slug': 'michael-ferrin'}' not found. 1 pattern(s) tried: ['fathers/lawyers/(?P<lawyer_slug>[\\w-]+)/createreview/$']
    
    • Klaus D.
      Klaus D. over 7 years
      Please add the full error traceback to your question!
    • Klaus D.
      Klaus D. over 7 years
      Your parameters in reverse inside of Review.get_absolute_url() are faulty. They look like they are for Lawyer.
    • user2901792
      user2901792 over 7 years
      Changed the parameters. Getting new error. Please see new traceback. The new url does not seem to be routing back to the original DetailView from CreateView after form submission.
    • solarissmoke
      solarissmoke over 7 years
      You are passing review_slug parameter in get_absolute_url, but the URL definition for that URL has no such parameter in it (it only has a lawyer_slug).
  • Sayyor Y
    Sayyor Y about 3 years
    Don't forget to from django.urls import reverse. Also, the first argument of reverse should be the name of the view specified in urls.py.