django reverse in formModel - django

In my views.py I have a class who take a (CreateView) and in the form_class take a Testform in forms.py
The problem is when in my forms.py I use the def save(...) method, it save correctly the data but the is no way to reverse after that
it say to me no matter what I try to return it says
"No URL to redirect to. Either provide a url or define a get_absolute_url method on the Model."
ok, so I've tried everywhere to put this method but no way, it allways gives me this error

It depends on where you want to redirect the user after creating a new object.
Example setup:
CreateView is configured with url r'^mymodel/new/', name='mymodel_create' in urls.py.
DetailView is configured with url r'^mymodel/:pk/', name='mymodel_detail' in urls.py.
You can define get_absolute_url like this:
class MyModel(Model):
# field definitions...
def get_absolute_url(self):
return reverse('mymodel_detail', kwargs={'pk': self.pk})
Creating a new object will then redirect to the DetailView.

Related

How can I skip pk in url while redirecting to another url with generic RedirectView in Django?

I am trying to delete a record after displaying it with DetailView and redirecting to the list page again.
view.py
...
class DailyRecordDeleteConformationView(RequiredBasicDetailsAndContextMixin, DetailView):
model = DailyRecord
obj_not_found_redirect = reverse_lazy('business:add_daily_records')
template_name = 'business/daily_records_detail.html'
context_object_name = 'record'
def get_object(self):
detail = self.model.custom_obj.get_single(self)
return detail
class DailyRecordDeleteView(RequiredBasicDetailsMixin, RedirectView):
pattern_name = 'business:daily_records'
def get_redirect_url(self, *args, **kwargs):
# Delete the record
return super().get_redirect_url(*args, **kwargs)
...
urls.py
...
path('daily-records/', DailyRecordView.as_view(), name='daily_records'),
path('daily-records/<int:pk>/', DailyRecordDeleteConformationView.as_view(), name='daily_record_detail'),
path('daily-records/<int:pk>/delete/', DailyRecordDeleteView.as_view(), name='daily_record_delete'),
...
Here I am getting this below error when click on the delete button on detail view
NoReverseMatch at /business/daily-records/7/delete/
Reverse for 'daily_records' with keyword arguments '{'pk': 7}' not found. 1 pattern(s) tried: ['business/daily\\-records/\\Z']
I am new to class based views, and still not able to figure out how to redirect to url that does not have the pk parameter.
I tryed to find any variable for RedirectView or something that can skip the pk of current url while redirecting to another url.
Thanks.
When you invoke get_redirect_url with **kwargs, your pk is in kwargs and in RedirectView, Django tries to get your url with that kwargs.
So if you don't want to pass the pk, just don't pass kawrgs:
def get_redirect_url(self, *args, **kwargs):
return super().get_redirect_url()
You can specify url instead of pattern_name, so:
from django.urls import reverse_lazy
class DailyRecordDeleteView(RequiredBasicDetailsMixin, RedirectView):
urls = reverse_lazy('business:daily_records')
# no get_redirect_url
Your get_redirect_url is also not supposed to delete records: the HTTP protocol specifies that GET and OPTION should be "safe" methods, that means they should not have side-effects.
You thus should delete records with a POST/DELETE request. You can work with a small FormView that will tackle this in case the (empty) form is valid, so by overriding the form_valid method.

Parsing POST request - object() takes no parameters

I try to access post request and check if user with email that is in post request already exist. When I try to send data to endpoint, I get error
TypeError: object() takes no parameters
my views.py
#csrf_exempt
class CheckIfEmailAvailable():
#csrf_exempt
def check(request):
email = request.POST.get("email")
if User.objects.filter(email=email).exists():
return Response({'status': 'not available'})
my url.py
url(r'^api/checkmail/', CheckIfEmailAvailable, name='check'),
What am I doing wrong ?
In this case better to use function in the url
from your_app.views import check
url(r'^api/checkmail/', check, name='check')
and your view will be like this (only function)
#csrf_exempt
def check(request):
email = request.POST.get("email")
if User.objects.filter(email=email).exists():
return JsonResponse({'status': 'not available'})
Also FYI if you want to use #csrf_exempt with classes you should use dispatch, you can get more info here
Example of JsonResponse
from django.http import JsonResponse
def your_view(request):
return JsonResponse({'foo':'bar'})
You need to inherit your class your generic django views for this to work. Url patterns expect a callable that can accept request (with args and kwargs) and return response. View.as_view class method returns a callable for you.
# In views file
from django.views.generic import View
...
class CheckIfEmailAvailable(View):
def get(self, request, *args, **kwargs):
return self.check(request) # or something else depends on your use case
# In urls file
from django.views.decorators.csrf import csrf_exempt
...
url(r'^api/checkmail/', csrf_exempt(CheckIfEmailAvailable.as_view()), name='check'),
Moreover csrf_exempt decorator won't work (out of box) on classes, neither on bound methods for that matter. Better way should be to use them in urls.
If you had posted the full traceback, you would see that the error does not come from parsing the request; Django does not even get nearly that far.
You can't use a class as a view like that. You should have check as a standalone function, without a class, and refer to it directly in your urls.
Django does support class-based views, but they have a very specific structure, need to inherit from the proper base class, and be referenced in the urls.py in a specific way.

Accessing the Object in the UpdateView to pass pk for success_url

I want to the user to an view which takes an pk as argument.
I want to use the self.object in the UpdateView to look up the pk and pass ist as args to the lazy_reverse at the success_url.
First of all, is there a better way to do it, and second, how do I access the Object?
The easiest option is to add a get_absolute_url method to your model. Then the UpdateView will redirect to this, and you don't need to set success_url at all.
class MyModel(models.Model):
...
get_absolute_url(self):
return reverse('view_mymodel', args=(self.pk,))
If you don't want to do this, then you can't use success_url and reverse_lazy here, because the url changes for each view depending on the primary key.
Instead, you can use get_success_url. You can access the object with self.object.
def get_success_url(self):
return reverse('view_mymodel', args=(self.object.pk,))

Writing a Django detail view with a generic class based view

I know this is really simple, but I'm missing something. And because I can't ever remember this, I'm hoping this can document a solution here.
All I want to do is pass a PK for the object in the URL and get the detail view back.
Url:
url(regex=r'^(?P<pk>\d+)/$',
view=AdventureDetail.as_view(),
name='adventure_detail',
),
View:
class AdventureDetail(DetailView):
""" Get a time entry detail view """
template_name = "adventure/AdventureDetail.html"
def get_object(self):
return get_object_or_404(Page)
But I'm getting a "multiple objects returned error"
MultipleObjectsReturned at /1/
get() returned more than one Page -- it returned 5! Lookup parameters were {}
This feels really silly. It should "just work" but I'm missing something obvious.
Thanks for the help.
In DetailView it is more simpler: you can just specify the model:
class AdventureDetail(DetailView):
""" Get a time entry detail view """
model = Page
template_name = "adventure/AdventureDetail.html"
And that's all. DetailView will do the rest of work.
Another way is to specify queryset:
class AdventureDetail(DetailView):
""" Get a time entry detail view """
queryset = Page.objects.all()
template_name = "adventure/AdventureDetail.html"
This will have the same result.
And the last way is to override the get_object method.
Look here for details
You're not passing any other parameters to get_object_or_404, other than the Page class. So now you're basically querying for all pages. So you'd need to do:
return get_object_or_404(Page, pk=self.kwargs.get('pk', None))
Also, why are you overriding get_object? The DetailView already contains this functionality so all you need to do is have a URL with pk in it.
In views.py
from django.views.generic import DetailView
# Import your model that you want to use in details view for example
from .models import Post
class PostDetailView(DetailView):
model = Post
Then create a template with the following name conversion
<appname>/<model_viewtype>.html
In urls.py
First import the class view that you created. In our case it is
from .views import PostDetailView
Then
path("post/<int:pk>/", views.PostDetailView.as_view(), name="PostDetailView")

Override Django Admin URLs for Specific Model?

First a little background:
I have an Event model that has various event_types. I want to break one of those event types, 'Film', into it's own admin. I have the basic functionality in place: a proxy model inheriting from Event, named Film, a custom manager for that proxy model that filters it to only 'film' event types, and it's own ModelAdmin.
The problem is with the reverse. I now need to filter out films from the main Event admin. I don't want to alter the Event model or its default manager, because the impact would be too widespread. So, I tried creating another proxy model, EventAdminProxy, with the sole purpose of providing a filtered list of events in the admin. I then register this model, instead of Event, with the existing ModelAdmin.
This obviously works, but it has the unfortunate side-effect of altering the URLs in the admin. Instead of the changelist being at "/admin/event/event/", it's now at "/admin/event/eventadminproxy/".
What I'm trying to do is keep this setup, but also keep the old URL. I've tried overloading the ModelAdmin's get_urls method, but from what I can tell, you can't control the full URL there, only what comes after the "/app_label/model_class/" part.
I thought about overriding it in the main urls.py, but can't figure out an acceptable view to tie into. The actual views are only available on the instantiated ModelAdmin object, not the class itself.
Any ideas of how override the URL being used in the admin?
Looking at the Django source, the admin URLs are built in two places, in the ModelAdmin instances, and in the AdminSite instances.
The part you want to change is built in the AdminSite instance (django.contrib.admin.sites.AdminSite), you can subclass that and override the get_urls method. If you look at the second half of the method you'll see this:
# Add in each model's views.
for model, model_admin in self._registry.iteritems():
urlpatterns += patterns('',
url(r'^%s/%s/' % (model._meta.app_label, model._meta.module_name),
include(model_admin.urls))
)
There it is adding the model's ._meta.module_name which is just the model's name lowercased (django.db.models.options.Options.contribute_to_class).
An easy way out is to override the Site's get_urls method and add a dict or special case for the Proxy model so it uses a different url instead of model._meta.module_name, something along the lines:
class MyAdminSite(AdminSite):
module_name_dict = {
EventAdminProxy: 'myfunkymodulename'
}
def get_urls(self):
base_patterns = super(MyAdminSite, self).get_urls()
my_patterns = patterns('',)
for model, model_admin in self._registry.iteritems():
if model in self.module_name_dict:
module_name = self.module_name_dict[model]
my_patterns += patterns('',
url(r'^%s/%s/' % (model._meta.app_label, module_name),
include(model_admin.urls))
)
return my_patterns + base_patterns
You could override the queryset-method of your EventModelAdmin and filter the queryset so that Film-Events get excluded.
Something similar to this:
class EventAdmin(admin.ModelAdmin):
def queryset(self, request):
qs = super(EventAdmin, self).queryset(request)
return qs.exclude(event_type='film')
You could also subclass ChangeList and override the url_for_result() method to customise change urls, (learned from another answer), e.g.:
from django.contrib.admin.views.main import ChangeList
class FooChangeList(ChangeList):
def url_for_result(self, obj):
return '/foos/foo/{obj.pk}/'
class FooAdmin(admin.ModelAdmin):
def get_changelist(self, request, **kwargs):
return FooChangeList
Adapted example for the question:
from django.contrib.admin.views.main import ChangeList
from django.urls import reverse
class FilmAdmin(admin.ModelAdmin):
def get_changelist(self, request, **kwargs):
class FilmChangeList(ChangeList):
def url_for_result(self, obj):
return reverse('admin:events_event_change', args=(obj.pk, ))
return FilmChangeList