Django reverse causing url circular import, why? - django

I get this error:
The included urlconf 'fouraxis.urls' does not appear to have any
patterns in it. If you see valid patterns in the file then the issue
is probably caused by a circular import.
I know the url pattern has something in it, it looks like this:
from django.conf.urls import include, url
from django.contrib import admin
urlpatterns = [
url(r'^perfil/', include('clientes.urls'), namespace="cliente"),
url(r'^admin/', include(admin.site.urls))
]
clientes.urls:
from django.conf.urls import url
from django.contrib.auth import views as auth_views
from clientes import views
urlpatterns = [
# login
url(r'^login/$', auth_views.login, {'template_name': 'perfiles/login.html'}, name="login"),
url(r'^logout/$', auth_views.logout, {'template_name': 'perfiles/logged_out.html'}, name="login"),
url(r'^mi_perfil/$', views.mi_perfil, name="mi_perfil"),
url(r'^registro_usuario/$', views.RegistroUsuario.as_view(), name="registro_usuario")
]
The RegistroUsuario view looks like this:
class RegistroUsuario(FormView):
template_name = "perfiles/registro_usuario.html"
form_class = UserCreationForm
success_url = reverse("cliente:mi_perfil") # THIS REVERSE
def form_valid(self, form):
return redirect("cliente:mi_perfil")
context = {'form': UserCreationForm}
I understand I can replace the reverse with a plain-text url like this perfil/mi_perfil. But, I want to know why is this happening with reverse, I can't find the explanation on de docs. Also, using reverse is better cause it is dynamic (if anytime I change the url, it still works as long as it keeps its name).

The reverse() call is made when the view is imported, which is probably when the urlconf is first loaded. You need to use reverse_lazy() instead:
from django.core.urlresolvers import reverse_lazy
class RegistroUsuario(FormView):
template_name = "perfiles/registro_usuario.html"
form_class = UserCreationForm
success_url = reverse_lazy("cliente:mi_perfil") # THIS REVERSE
def form_valid(self, form):
return redirect("cliente:mi_perfil")
context = {'form': UserCreationForm}

Related

Django 1.10: Redirect to another view or url not working. django.urls.exceptions.NoReverseMatch

In Django 1.10, My redirect to other view via url or the viewname is not working.
The main urls.py file is as below:
urlpatterns = [
url(r'^', include('app1.urls')),
]
The urls.py file inside app1 is:
from django.conf.urls import url
from . import views
app_name = 'app1'
urlpatterns = [
url(r'^$', views.home, name='home'),
url(r'^details/$', views.details, name='details'),
]
The views.py for app1 is as below:
from django.shortcuts import render, redirect
from django.utils.timezone import now
import datetime
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse
def home(request):
if request.method == 'POST':
print("redirected from home..................")
url = reverse('details')
return HttpResponseRedirect(url)
# return redirect('app1:details')
# return redirect('/details/')
# return redirect('details')
print("Not redirected..................")
return render(request, "app1/index.html", {})
def details(request):
print("Redirect OK inside details")
today = datetime.date.today()
return render(request, "app1/details.html", {
'today': today,
'now': now(),
'email_text': request.POST.get('email_item', 'bla bla'),
})
I tried all the options in the home view with the commented code like using both HttpResponseRedirect and redirect but not able to redirect to the details view.
I get the error:
django.urls.exceptions.NoReverseMatch: Reverse for 'details' with
arguments '()' and keyword arguments '{'request': <HttpRequest>}' not found. 0 pattern(s) tried: []
Any suggestions would help:
I'm sure you found a solution long ago, but I've struggled with this problem before and I came up with a simple solution that doesn't require you to create a view that exists solely to redirect you to another view. Hopefully it helps some people out who need this question answered still. In general, it goes like this:
from django.shortcuts import redirect
urlpatterns = [
url(r'^$', lambda request: redirect('my_redirect_url_name')),
url(r'^my_redirect_url/$', views.redirect_view, name='my_redirect_url_name'))
]
I'm not sure why, but this does not work if you only pass redirect(); it must be contained within a function such as a lambda function.
If you want to do something as simple as redirect them to the login page you can use the login_required decorator or another a custom login lambda function:
from django.shortcuts import redirect
from django.contrib.auth.decorators import login_required
urlpatterns = [
url(
r'^$',
login_required(lambda request: redirect('my_redirect_url_name'),
redirect_field_name='my_login_url_name')
),
# this custom redirect function will behave like login_required()
url(
r'^$',
lambda request: redirect('my_redirect_url_name') if request.user and \
request.user.is_authenticated() else redirect('my_login_url_name')
),
url(r'^my_redirect_url/$', views.redirect_view, name='my_redirect_url_name')),
url(r'^my_login_url/$', views.login_view, name='my_login_url_name'))
]
Specifically in answer to your question, I think this will give the redirect you want. This in urls:
from django.shortcuts import redirect
urlpatterns = [
url(
r'^$',
lambda request: redirect('index') if request.method == 'POST' else \
redirect('details'),
name='home'
),
url(r'^details/$', views.details, name='details'),
url(r'^index/$', views.index, name='index'),
]
With this added to views:
def index(request):
return render(request, "app1/index.html", {})
Just FYI, I have not tested this with a redirect using if request.method == 'POST', but I think it will work.
Note the working example within your own code.
By specifying the app name you need to use namespacing with reverse.
reverse('details')
should read
reverse('app1:details')
See the documentation over here:
https://docs.djangoproject.com/en/1.10/ref/urlresolvers/

django reverse with namespace

I'm getting this error:
The included urlconf 'unsitiodigital.urls' does not appear to have any patterns in it.
The traceback points to this line
class Contacto(FormView):
template_name = "contacto.html"
form_class = FormContacto
success_url = reverse("main:mensaje_enviado") -->This Line
def form_valid(self, form):
form.send_email()
return super(Contacto, self).form_valid(form)
There are valid patterns, it works without the reverse line.
urls.py - general
urlpatterns = [
url(r'^', include('main.urls', namespace="main")),
url(r'^admin/', include(admin.site.urls)),
]
urls.py - main
from django.conf.urls import url
from main import views
urlpatterns = [
url(r'^$', views.Inicio.as_view(), name='inicio'),
url(r'^quienes_somos/$', views.QuienesSomos.as_view(), name='quienes_somos'),
url(r'^opciones/$', views.Opciones.as_view(), name='opciones'),
url(r'^contacto/$', views.Contacto.as_view(), name='contacto'),
-> url(r'^mensaje_enviado/$', views.MensajeEnviado.as_view(), name='mensaje_enviado')
]
So, ¿which is the correct user of reverse?. Thanks a lot.
The include path must be wrong
url(r'^', include('main.urls', namespace="main")), # the 'main.urls' path must be wrong
There are some ways you can include other urls. Try to import the patterns from the main.url module instead
from main.urls import urlpatterns as main_urls
url(r'^', include(main_urls, namespace="main"),
You also have to use reverse_lazy in the success_url.
from django.core.urlresolvers import reverse_lazy
class Contacto(FormView):
template_name = "contacto.html"
form_class = FormContacto
success_url = reverse_lazy("main:mensaje_enviado")
It is useful for when you need to use a URL reversal before your project’s URLConf is loaded.

Django does not match urls

I've been working on a django web app (using Django 1.3), a web catalogue of products, and it was working fine until I was asked to add a custom admin site. I'm fully aware of the Django admin site, but the client is very old-fashioned, and is tech-ignorant so I have to make a "for dummies" version admin site. The root urlconf:
from django.conf.urls.defaults import *
from django.views.generic import TemplateView
from store.models import Category
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
url(r'^admin/', include(admin.site.urls)),
url(r'^^$', TemplateView.as_view(
template_name='homepage.html',
get_context_data=lambda: {
'crumb': 'home',
'category_list':Category.objects.all()
}
),
name='home'),
url(r'^favicon\.ico$', 'django.views.generic.simple.redirect_to', {'url': '/static/img/favicon.ico'}),
url(r'^store/', include('store.urls', app_name='store', namespace='store')),
)
And the urlconf for the store app:
from django.conf import settings
from django.conf.urls.defaults import *
from store import views
urlpatterns = patterns ('',
url(r'^category/$', views.get_brands, name='get_brands'),
url(r'^(\w+)/$', views.GalleryView.as_view(), name='gallery'),
url(r'^(\w+)/(\w+)/$', views.GalleryView.as_view(), name='gallery'),
)
and the original views:
from django.http import Http404
from django.shortcuts import render, get_object_or_404
from django.views.generic import ListView
from store.models import Category, Brand, Product
def get_brands(request):
q = request.GET.get('q')
if q is not None:
category = get_object_or_404(Category, slug__iexact=q)
try:
brands = category.brands.all()
except:
brands = []
template = 'infobox.html'
data = {
'category': category,
'brands': brands,
}
return render( request, template, data )
class GalleryView(ListView):
context_object_name = 'product_list'
template_name = 'store/gallery.html'
def get_queryset(self):
self.category = get_object_or_404(Category, slug__iexact=self.args[0])
try:
brand = Brand.objects.get(slug__iexact = self.args[1])
self.brand_name = brand.name
except:
#no brand is specified, show products with no brand
if self.category.category_products.filter(brand__isnull=True):
#if there are products with no brand, return those
return self.category.category_products.filter(brand__isnull=True)
else:
#if all products have a brand, return the products of the first brand
all = self.category.brands.all()
if all:
brand = all[0]
self.brand_name = brand.name
return brand.brand_products.all()
else:
raise Http404
else:
#brand is specified, show its products
return Product.objects.filter(category=self.category, brand=brand)
def get_context_data(self, **kwargs):
context = super(GalleryView, self).get_context_data(**kwargs)
category = self.category
category_brands = self.category.brands.all()
context['category_list'] = Category.objects.all()
context['category'] = category
context['crumb'] = category.name
context['category_brands'] = category_brands
try:
context['brand'] = self.brand_name
except:
context['brand'] = None
return context
Now, my custom admin app was working fine on my local dev environment, but when I added the new urls and views to prod, Django doesn't seem to match any of the new urls. The original views and urls still work, but none of the new urls get matched and I just keep getting a 404 Not Found error.
The updated urlconf:
from django.conf import settings
from django.conf.urls.defaults import patterns, include, url
from django.contrib.auth.decorators import login_required
from store import views
admin_urls = patterns ('',
url(r'^$', login_required(views.AdminIndexView.as_view()), name='admin_index'),
url(r'^add/(\w+)/$', login_required(views.AdminAddView.as_view()), name='admin_add'),
)
urlpatterns = patterns ('',
url(r'^category/$', views.get_brands, name='get_brands'),
url(r'^(\w+)/$', views.GalleryView.as_view(), name='gallery'),
url(r'^(\w+)/(\w+)/$', views.GalleryView.as_view(), name='gallery'),
url(r'^login/$', views.admin_login, name='login'),
url(r'^logout/$', views.admin_logout, name='logout'),
url(r'^logout/success/$', views.admin_logout_success, name='logout_success'),
url(r'^test/', views.test, name='test'),
url(r'^admin/', include(admin_urls, namespace='admin')),
url(r'^ajax/$', views.ajax_request, name='ajax_request'),
)
Note that not even the simple '/store/test/' url does not get matched. I'm not really sure why Django isn't matching my urls, and any help is appreciated.
I'm not exactly sure what was happening since all of my templates linked to other pages using the {% url %} tag, but I think the reason my urls were getting muddled up was because I was using the r'^(\w+)/$' regex, so it was matching any word. I simply moved the urlconfs with the (\w+) rule to the bottom of urls.py, and refactored them to be a little more specific and they were gold again.
The updated urlconf:
from django.conf import settings
from django.conf.urls.defaults import patterns, include, url
from django.contrib.auth.decorators import login_required
from store import views
admin_urls = patterns ('',
)
urlpatterns = patterns ('',
url(r'^category/$', views.get_brands, name='get_brands'),
url(r'^(\w+)/$', views.GalleryView.as_view(), name='gallery'),
url(r'^(\w+)/(\w+)/$', views.GalleryView.as_view(), name='gallery'),
url(r'^login/$', views.admin_login, name='login'),
url(r'^logout/$', views.admin_logout, name='logout'),
url(r'^logout/success/$', views.admin_logout_success, name='logout_success'),
url(r'^ajax/$', views.ajax_request, name='ajax_request'),
url(r'^administration/$', login_required(views.AdminIndexView.as_view()), name='admin_index'),
url(r'^administration/add/(\w+)/$', login_required(views.AdminAddView.as_view()), name='admin_add'),
)
As you mentioned in the comments, removing login_required fixes the problem. Here's what the django docs have to say about the decorator:
login_required() does the following:
If the user isn’t logged in, redirect to settings.LOGIN_URL, passing the current absolute path in the query string. Example:
/accounts/login/?next=/polls/3/.
If the user is logged in, execute the view normally
[...] Note that if you don't specify the login_url parameter, you'll
need to map the appropriate Django view to settings.LOGIN_URL.
In other words, it goes to a default url you can override in settings.LOGIN_URL.
Now here's what I think happened - I think you defined your own login here:
url(r'^login/$', views.admin_login, name='login'),
And because the login_required in the new urls pointed at the default url, which doesn't exist it returned 404. However, when you configured the login_required views after your other urls:
url(r'^login/$', views.admin_login, name='login'),
...
url(r'^administration/$', login_required(views.AdminIndexView.as_view()), name='admin_index'),
url(r'^administration/add/(\w+)/$', login_required(views.AdminAddView.as_view()), name='admin_add'),
it worked. Why is that? You didn't overrid LOGIN_URL here right? But you sort of did - because at this point, you redefined the namespace 'login' to point to your own view. Now this isn't mentioned anywhere in the docs, but it makes sense, and looking at the default admin templates you can see this is the namespace being used.
Does that make sense to you?

Where to store my index.html and index view for a non-static Django homepage

I'm creating a Django project and currently have 3 apps (products, questions and choices) - I've got them all functioning separately/together as I'd like and am using namespaces and include as part of my urls (my urls.py given below...)
### urls.py
from django.conf.urls import patterns, include, url
from django.contrib import admin
from django.views.generic import TemplateView
admin.autodiscover()
urlpatterns = patterns('',
url(r'^products/', include('products.urls', namespace='products')),
url(r'^questions/', include('questions.urls', namespace='questions')),
url(r'^choices/', include('choices.urls', namespace='choices')),
url(r'^admin/', include(admin.site.urls)),
)
Now I'm looking to add an index page (accessible at localhost:8000/) that will allow me to access all models created. I'm happy with my views.py...
## views.py
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.views import generic
from products.models import Product
from questions.models import Question
from choices.models import Choice
class IndexView(generic.ListView):
template_name = 'index.html'
context_object_name = 'latest_product_list'
def get_queryset(self):
return Product.objects.all()
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
context['questions'] = Question.objects.all()
context['choices'] = Choice.objects.all()
return context
What I'd like to know is, what is the most sensible combination of urls.py entry, positioning of views.py and location of index.html to allow me to show a combination of my models on the landing page?
Thanks,
Matt
Create another app (I tend to name it home). That way, your home app can import models from products, questions and choices.
App layout
home/urls.py
home/views.py
home/templates/index.html
Include your home urls just like your other apps (but don't use a prefix)
url(r'^', include('home.urls', namespace='home')),

Django: How can i get the url of a template by giving the namespace?

While i'm rendering a template i would like to retrieve the url of the template by giving the namespace value and not the path. For example instead of this:
return render(request, 'base/index.html', {'user':name})
i would like to be able to do the following:
from django.shortcuts import render
from django.core.urlresolvers import reverse
return render(request, reverse('base:index'), {'user':name})
but the above produces an error. How can i do it? Is there any way to give the namespace to a function and get the actual path?
Extended example:
- urls.py
from django.conf.urls.defaults import patterns, include, url
urlpatterns = patterns('',
url(r'^', include('base.urls', namespace='base')),
)
- app base: urls.py
from django.conf.urls.defaults import patterns, url
urlpatterns = patterns('base.views',
url(r'^/?$', 'index', name='index'),
)
- app base: views.py
from django.shortcuts import render
from django.core.urlresolvers import reverse
def homepage(request):
'''
Here instead of 'base_templates/index.html' i would like to pass
something that can give me the same path but by giving the namespace
'''
return render(request, 'base_templates/index.html', {'username':'a_name'})
Thanks in advance.
Template names are hard coded within the view. What you can also do is that you can pass the template name from the url pattern, for more details see here:
from django.conf.urls.defaults import patterns, url
urlpatterns = patterns('base.views',
url(r'^/?$', 'index',
{'template_name': 'base_templates/index.html'},
name='index'),
)
Then in view get the template name:
def index(request, **kwargs):
template_name = kwargs['template_name']