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

user2901792 picture user2901792 · Aug 9, 2016 · Viewed 13.4k times · Source

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/$']

Answer

nkhumphreys picture nkhumphreys · Aug 9, 2016

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