I've burrowed through the mound of NoReverseMatch questions here on SO and elsewhere, but to no avail.
I have a view method, clean, and within in a redirect:
def clean(request, aid):
if request.method == 'POST':
return redirect(enrich,
permanent=True,
aid=account.id)
else:
return render(request, 'clean.html')
And a view method called enrich:
def enrich(request, aid):
return HttpResponse('this be the enrich page')
It has a path in urls.py:
path('<aid>/enrich/', views.enrich, name='enrich'),
And yet, when calling on the redirect in the clean method, I am lovingly told this by Python:
NoReverseMatch at /app2/<aid>/clean/
Reverse for 'app2.views.enrich' not found. 'app2.views.enrich' is not a valid view function or pattern name.
Which leaves me flummoxed, as app2.views.enrich does indeed exist. What am I to do? The path exists and operates correctly (if I visit /app2/<aid>/add/, I am welcomed with the proper HTTP response), but on redirect it doesn't actually seem to work, and neither do any of the other views.
Some context:
There are 3 apps in this Django project
All of these apps have proved functional along with their views
The versioning is Django 2.1 and Python 3.7.1
Disclaimer: the app is not actually called 'app2', that is simply a placeholder.
The wrong in this program is redirect(enrich, ...), the redirect first argument can't be a view function or view class.
ref from https://docs.djangoproject.com/en/2.1/_modules/django/shortcuts/#redirect:
The arguments could be:
A model: the model's get_absolute_url() function will be called.
A view name, possibly with arguments: urls.reverse() will be used
to reverse-resolve the name.
A URL, which will be used as-is for the redirect location.
The error message of Reverse for 'app2.views.enrich' not found, is because it print the view function's qualified name.
You should use str 'enrich' as first argument in redirect().
In the context of multiple apps, one has to specify the app namespace in the redirect.
return redirect('app2:enrich', ...
Related
I am implementing Django two-factor-auth on my website and I would love to have some views protected by two-FA, and some other not.
In order to do so, I use the decorator #otp_required which works great, but unfortunately asks the users to input their credentials again (to handle user sessions, I use the registration module).
Would you be able to give me a good to way to hack the form in order to just ask the user to input the token (skipping a step of the form, basically) ?
Thanks a lot,
For those who care, I found a way to do it that is quite clean.
The trick was to override the LoginView class in the core.py module of the two_factor_authentication module.
In order to do so, go to your views and insert the following code:
class CustomLoginView(LoginView):
form_list = (
('token', AuthenticationTokenForm),
('backup', BackupTokenForm),
)
def get_user(self):
self.request.user.backend = 'django.contrib.auth.backends.ModelBackend'
return self.request.user
Basically, I erase the 'auth' step and override the method get_user() in order to return the current user.
The backend must be specified otherwise Django raises an error.
Now, to make that class be used instead of the LoginView, go to your urls and insert the following line BEFORE including the two_factor.urls.
url(r'^account/login/$', tradingviews.CustomLoginView.as_view(), name='login'),
That's it!
Django newbie here, need help on basic middleware to redirect to another view if a certain model field is empty.
I am creating a terms of agreement page that users must get redirected to right after they signup to the platform if their filed_terms field on their Profile model is empty.
I am using middleware for this. However I am unable to get this to work. This is my middleware class:
class TermsMiddleware(object):
def process_request(self, request):
if request.user.profile.filled_terms is None:
return redirect(reverse(terms))
This gives me the following error:
global name 'terms' is not defined
I also have the url matcher that works perfectly when I navigate to it manually:
url(r'^terms/', 'my_app.views.terms')
I have a terms.html template and a terms view in my views.py file that is working perfectly in all other respects. I have also added it to the settings middleware requirements to make sure it loads.
Do I have to import something from views or url dispatcher into my middleware file? If so what would that be? I have been at this for a while an cannot find anything helpful.
reverse function takes url name instead on the regex. So you need to add name on your url configuration. Here is the example.
url(r'^terms/', 'my_app.views.terms', name='terms')
Add this in your views.py
from django.core.urlresolvers import reverse
And you need to fix your reverse function into.
return redirect(reverse('terms'))
Python interpret your terms as a variable and you have no variable named terms while you need to put string on reverse.
When defining URL patterns, I am supposed to use a regular expression to acquire a PK from the URL.
What if I want a URL that has no PK, and if it's not provided, it will use the currently logged in user? Examples:
visiting /user will get a DetailView of the currently logged in user
/user/edit will show an UpdateView for the currently logged in user
I tried hard-coding the pk= in the Detail.as_view() call but it reports invalid keyword.
How do I specify that in the URL conf?
My sample code that shows PK required error when visiting /user URL:
urlpatterns = patterns('',
url(r'user/$',
DetailView.as_view(
model=Account,
template_name='user/detail.html')),
)`
An alternative approach would be overriding the get_object method of the DetailView subclass, something along the line of:
class CurrentUserDetailView(UserDetailView):
def get_object(self):
return self.request.user
Much cleaner, simpler and more in the spirit of the class-based views than the mixin approach.
EDIT: To clarify, I believe that two different URL patterns (i.e. one with a pk and the other without) should be defined separately in the urlconf. Therefore they could be served by two different views as well, especially as this makes the code cleaner. In this case the urlconf might look something like:
urlpatterns = patterns('',
url(r"^users/(?P<pk>\d+)/$", UserDetailView.as_view(), name="user_detail"),
url(r"^users/current/$", CurrentUserDetailView.as_view(), name="current_user_detail"),
url(r"^users/$", UserListView.as_view(), name="user_list"),
)
And I've updated my example above to note that it inherits the UserDetailView, which makes it even cleaner, and makes it clear what it really is: a special case of the parent view.
As far as I know, you can't define that on the URL definition, since you don't have access to that information.
However, what you can do is create your own mixin and use it to build views that behave like you want.
Your mixin would look something like this:
class CurrentUserMixin(object):
model = Account
def get_object(self, *args, **kwargs):
try:
obj = super(CurrentUserMixin, self).get_object(*args, **kwargs)
except AttributeError:
# SingleObjectMixin throws an AttributeError when no pk or slug
# is present on the url. In those cases, we use the current user
obj = self.request.user.account
return obj
and then, make your custom views:
class UserDetailView(CurrentUserMixin, DetailView):
pass
class UserUpdateView(CurrentUserMixin, UpdateView):
pass
Generic views uses always RequestContext. And this paragraph in the Django Documentation says that when using RequestContext with auth app, the template gets passed an user variable that represents current user logged in. So, go ahead, and feel free to reference user in your templates.
You can get the details of the current user from the request object. If you'd like to see a different user's details, you can pass the url as parameter. The url would be encoded like:
url(r'user/(?P<user_id>.*)$', 'views.user_details', name='user-details'),
views.user_details 2nd parameter would be user_id which is a string (you can change the regex in the url to restrict integer values, but the parameter would still of type string). Here's a list of other examples for url patterns from the Django documentation.
I'm curious about setting urls accept_method directly when defining url like
url(r'some-pattern', 'view_name', name='url_name', accept_method='GET|POST')
I know there're view decorators like require_GET, require_POST etc.
Is there any solutions for Django or any extension to do it?
Thanks.
Yes it can be done using django middleware.
Use Process Request to intercept the requests.
It should return either None or an HttpResponse object. If it returns
None, Django will continue processing this request, executing any
other process_request() middleware, then, process_view() middleware,
and finally, the appropriate view. If it returns an HttpResponse
object, Django won't bother calling any other request, view or
exception middleware, or the appropriate view; it'll apply response
middleware to that HttpResponse, and return the result.
HttpRequest has a path attribute which contains the url. Use that url to lookup the urlconf and check the urlconf params for the accept-method and return None or the same HttpResponse accordingly.
#Pratik Mandrekar thanks for definitive information and documentation.
I've created a small application which does this kind of job https://github.com/imanhodjaev/django-besmart-common
I've noticed a strange behavior with how Django is processing my url patterns. A user should login and then be redirected to their profile page. I also have the ability for a user to edit their profile.
Here are my URL patterns for one of my apps:
urlpatterns=patterns('student.views',
(r'profile/$', login_required(profile,'student')),
(r'editprofile/$', login_required(editprofile,'student')),
)
This is for an app called student. If the user goes to /student/profile they should get the profile view. If they go to /student/editprofile they should get the editprofile view. I setup a function called login_required which does some checks on the user. It's a little more complicated than I could handle with just annotations.
Here's login_required:
def login_required(view,user_type='common'):
print 'Going to '+str(view)
def new_view(request,*args,**kwargs):
if(user_type == 'common'):
perm = ''
else:
perm = user_type+'.is_'+user_type
if not request.user.is_authenticated():
messages.error(request,'You must be logged in. Please log in.')
return HttpResponseRedirect('/')
elif request.user.is_authenticated() and user_type != 'common' and not request.user.has_perm(perm):
messages.error(request,'You must be an '+user_type+' to visit this page. Please log in.')
return HttpResponseRedirect('/')
return view(request,*args,**kwargs)
return new_view
Anyways, the weird thing is that, when I visit /student/profile, even though I get to the right page, login_required prints the following:
Going to <function profile at 0x03015DF0>
Going to <function editprofile at 0x03015BB0>
Why is it printing both? Why is it trying to visit both?
Even weirder, when I try to visit /student/editprofile, the profile page is what loads and this is what's printed:
Going to <function profile at 0x02FCA370>
Going to <function editprofile at 0x02FCA3F0>
Going to <function view_profile at 0x02FCA4F0>
view_profile is a function in a completely different app.
These two patterns:
(r'profile/$', login_required(profile,'student')),
(r'editprofile/$', login_required(editprofile,'student')),
Both match http://your-site/student/editprofile.
Try:
(r'^profile/$', login_required(profile,'student')),
(r'^editprofile/$', login_required(editprofile,'student')),
Django uses the view who's pattern matches first (see number 3 here).
Not sure why you can't use the standard #login_required decorator - it seems that your version actually provides less functionality, given that it always redirects to \, rather than the actual login view.
In any case, the reason why both are printed is because the print statement is in the top level of the decorator, and thus is executed when the urlconf is evaluated. If you put it in the inner new_view function, it will only be executed when it is actually called, and should print only the relevant view name.
Your login_required looks like it's a python decorator. Any reason you need to have it in your urls.py?
I think the print 'Going to '+str(view) line is getting evaluated when urlpatterns is read to determine which view to execute. It looks weird, but I don't think it'll hurt you.
The line print 'Going to '+str(view) will not be executed every time the view is hit, only when the url pattern is evaluated (I think). The code in new_view is the only code that will execute for certain as part of the view.