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/$']
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
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With