I am learning django by myself and created mini project for learning pupose and as example took a kidgarden.
I have a directorry sun and with two apps inside it accounts and equity apps.
In accounts i have templates with login signup htmls. This section works fine without any problem.
Your directory structure is still not clear. Do this. If it's works, please comment and I will explain what was wrong in your code.
in main dictionary (sun) urls.py, copy and paste this code.
from django.contrib import admin
from django.urls import path,include
from . import views
from accounts import views as account_views # added line
urlpatterns = [
path('admin/', admin.site.urls),
path('',views.HomePage.as_view(),name='home'),
path('accounts/',include('accounts.urls',namespace='accounts')),
path('accounts/',include('django.contrib.auth.urls')),
# path('kids/',views.TestPage.as_view(),name='kids'), # comment this url
path('kids/', account_views.home, name='kids'), # added url
path('thanks/',views.ThanksPage.as_view(),name='thanks'),
]
----- Edit ----
Initially, this was URL line. path('kids/',views.TestPage.as_view(),name='kids'). this was calling this function:
class ProductDetail(models.Model):
class TestPage(TemplateView):
template_name = 'equity/kids.html'
And your logic was written in this function (views in equity app):
def home(request):
stock = ChildForm()
if request.method == "POST":
stock = ChildForm(request.POST)
if stock.is_valid():
data = stock.save(commit=True)
name = data.name
context={
'name':name, }
else:
stock = ChildForm()
return render(request,'equity/kids.html',{'stock':stock})
return render(request,'equity/garden.html',context)
return render(request,'equity/kids.html',{'stock':stock)
You had to call right function which was in equity' views. So solution was this:
replace this path('kids/',views.TestPage.as_view(),name='kids')
with this:
path('kids/', account_views.home, name='kids')
importing views from equity is first thing.
from accounts import views as account_views
Related
I'm using path converter in my django app like so:
# urls.py
from . import views
from django.urls import path
urlpatterns = [
path('articles/<str:collection>', views.ArticleView),
]
# views.py
#login_required
def ArticleView(request, collection):
print(collection)
if collection == "None":
articles_query = ArticleModel.objects.all()
...
This works fine in development for a url suck as : http://localhost:8000/articles/My Collection which gets encoded to http://localhost:8000/articles/My%20Collection, and is decoded properly in the ArticleView. However, in development, I have to edit the view like so to get it to work:
# views.py
import urllib.parse
#login_required
def ArticleView(request, collection):
collection = urllib.parse.unquote(collection)
print(collection)
if collection == "None":
articles_query = ArticleModel.objects.all()
...
Otherwise, the print(collection) shows My%20Collection and the whole logic in the rest of the view fails.
requirements.txt
asgiref==3.2.10
Django==3.1.1
django-crispy-forms==1.9.2
django-floppyforms==1.9.0
django-widget-tweaks==1.4.8
lxml==4.5.2
Pillow==7.2.0
python-pptx==0.6.18
pytz==2020.1
sqlparse==0.3.1
XlsxWriter==1.3.3
pymysql
What am I doing wrong here?
Thanks in advance!
The URL is being urlencoded which encodes spaces as %20. There are a number of other encodings. As you've discovered you need to decode that parameter in order to compare it to what you'd expect. As you've likely realized, if you have a value that actually wants The%20News and not The News, you have no recourse. To handle this people will create a slug field. Django has a model field for this in the framework.
This is typically a URL-friendly, unique value for the record.
Assuming you add a slug = models.SlugField() to ArticleModel, your urls and view can change into:
urlpatterns = [
# Define a path without a slug to identify the show all code path and avoid the None sentinel value.
path('articles', views.ArticleView, name='article-list'),
path('articles/<slug:slug>' views.ArticleView, name='article-slug-list'),
]
#login_required
def ArticleView(request, slug=None):
articles_query = ArticleModel.objects.all()
if slug:
articles_query = articles_query.filter(slug=slug)
I want to load a default django page. Nothing fancy. However, the error I get, hints at an id that is incorrectly set.
"Field 'id' expected a number but got 'zoekboek'."
The confusing things here (I am a django beginner, so I wouldn't be surprised if this is not confusing at all for you):
the path for this page in the urls.py is not asking for an id.
the view is not querying anything yet (I found some posts that had similar errors,
but related to a filter).
the debug info points to another view that indeed is requesting an id.
when I add a slash at the beginning of the path, the error is gone!
The code
urls.py
urlpatterns = [
path('', views.scholen, name='scholen'),
path('<school_id>', views.school_detail, name='school_detail'),
path('<school_id>/<groep_id>', views.school_groep, name='school_groep'),
path('<school_id>/<groep_id>/<UserProfile_id>', views.leerling_page, name='leerling_page'),
path('zoekboek', views.zoekboek, name='zoekboek'),
]
views.py
from django.shortcuts import render, redirect, reverse, get_object_or_404
from books.models import Book, Rating
from .models import School, Groep
from profiles.models import UserProfile, Hobby, Sport
from django.contrib.auth.models import User
# Create your views here.
def scholen(request):
"""
Homepage for participating
schools.
"""
scholen = School.objects.all()
context = {
'scholen': scholen,
}
return render(request, 'schools/school_landing.html', context)
def school_detail(request, school_id):
"""
Details of individual schools.
"""
school = get_object_or_404(School, pk=school_id)
groep = Groep.objects.filter(school=school)
context = {
'school': school,
'groep': groep,
}
return render(request, 'schools/school_detail.html', context)
def school_groep(request, school_id, groep_id):
"""
Details of groep.
"""
school = get_object_or_404(School, pk=school_id)
groep = get_object_or_404(Groep, pk=groep_id)
a = groep.naam
kinderen = UserProfile.objects.filter(groep=a)
context = {
'school': school,
'groep': groep,
'kinderen': kinderen,
}
return render(request, 'schools/school_groep.html', context)
def leerling_page(request, school_id, groep_id, UserProfile_id):
"""
Personal page of school kids.
"""
profile = get_object_or_404(UserProfile, pk=UserProfile_id)
# If viewer is owner of page, viewer can edit
owner = False
if request.user == profile.user:
owner = True
context = {
'profile': profile,
'owner': owner,
}
return render(request, 'schools/leerling_page.html', context)
def zoekboek(request):
"""
Page for kids to search their favorite book
"""
context = {
}
return render(request, 'schools/zoek_boek.html', context)
Is this enough information?
Simple fix: move path('zoekboek', views.zoekboek, name='zoekboek'), from the last place to the second place in your urls.
Why?
Because Django URLs are resolved using regular expressions; the docs say here in point 3:
Django runs through each URL pattern, in order, and stops at the first one that matches the requested URL, matching against path_info.
Since your URL path path('<school_id>', views.school_detail, name='school_detail'), is very generic, it matches any string including the string zoekboek; so the request to zoekboek falls into the second line in your URL conf and gets routed to the view school_detail() and a school_id is expected for that view.
Suggestion: to make the URL handling easier and so you can order the URL paths however you like, you could change the URL a bit and add a prefix (for example school/) so that not any string matches the URL paths. For example, this schould work:
urlpatterns = [
path('', ...),
path('school/<school_id>', ...),
path('school/<school_id>/<groep_id>', ...),
path('school/<school_id>/<groep_id>/<UserProfile_id>', ...),
path('zoekboek', ...),
]
I'm trying to get django nested admin to work, but I'm having a few issues and I'm sure I'm just making a silly mistake. Here are the steps that I followed:
Step 1: I did a pip install
Step 2: I added it to the bottom of my Installed Apps in my settings.py
Step 3: I added it to my URL array:
Their Example:
urlpatterns = patterns('',
# ...
url(r'^_nested_admin/', include('nested_admin.urls')),
)
My implementation:
urlpatterns = [
path('admin/', admin.site.urls),
path('', include("estimate_maker.urls")),
path('nested_admin/', include('nested_admin.urls')),
]
Step 4: I created a static folder in my settings.py
Step 5: I ran the collectstatic command
Step 6: I set up my admin.py in my project folder:
from django.contrib import admin
from .models import MoldInspection, MoldService
import nested_admin
class MoldServiceInline(nested_admin.NestedStackedInline):
model = MoldService
class MoldInspectionInline(nested_admin.NestedModelAdmin):
model = MoldService
sortable_field_name = "position"
inlines = [MoldServiceInline]
admin.site.register(MoldInspection)
admin.site.register(MoldService)
I do get a warning from pycharm saying the below that I'm not sure how to diagnose as I'm setting up the class as it is done in the guide.
Cannot find reference 'NestedModelAdmin' in '__init__.py'
Looking in the referenced __init__.py I see:
# import mapping to objects in other modules
all_by_module = {
'nested_admin.forms': (
'SortableHiddenMixin'),
'nested_admin.formsets': (
'NestedInlineFormSet', 'NestedBaseGenericInlineFormSet'),
'nested_admin.nested': (
'NestedModelAdmin', 'NestedModelAdminMixin', 'NestedInlineAdminFormset',
'NestedInlineModelAdmin', 'NestedStackedInline', 'NestedTabularInline',
'NestedInlineModelAdminMixin', 'NestedGenericInlineModelAdmin',
But when I update my admin.py to:
class MoldInspectionInline(nested_admin.nested.NestedModelAdmin):
I get the same error, this time pointing to "nested."
When I try to access the nested admin by either going to /nested-admin, I just get a 404 with this error message:
Using the URLconf defined in app.urls, Django tried these URL patterns, in this order:
admin/
[name='home']
nested-admin ^server-data\.js$ [name='nesting_server_data']
And when I go to /admin it looks the same as it did before.
A few more details:
I want my MoldService to exist just to be a parent for children services so I have it set up like this:
class MoldService(models.Model):
title = "Mold Services"
def __str__(self):
return self.title
I then have my child class set up like this:
class MoldInspection(models.Model):
title = "Mold Inspection"
description = models.TextField(null=True)
def __str__(self):
return self.description
Why do you think the nested admin isn't working for me?
In the referenced __init__.py file it contains the following comment
# All this craziness is so that we can allow the classes in nested_admin.formsets
# to be importable directly from this module
I guessed that something going on here is the issue, so I just deleted the contents of __init__.py and then imported with
from nested_admin.nested import *
works now!
Is there any way to get a list of all views in an django app? I have googled for answer. All answers shows a way to get list of urls.
Getting list of all the views of a Django project:
To get all the views present in a Django project, we create a function get_all_view_names() which takes urlpatterns as input and returns the complete list of views being used in the project as the output.
First, we import the root_urlconf module using settings.ROOT_URLCONF. Then root_urlconf.urls.urlpatterns will give us the list of project's urlpatterns.
The above urlpatterns list contains RegexURLPattern and RegexURLResolver objects. Accessing .urlpatterns on a RegexURLResolver will further give us a list of RegexURLPattern and RegexURLResolver objects.
A RegexURLPattern object will give us the view name which we are interested in. The callback attribute on it contains the callable view. When we pass either a string in our urls like 'foo_app.views.view_name' representing the path to a module and a view function name, or a callable view, then callback attribute is set to this. Further accessing .func_name will give us the view name.
We call the function get_all_view_names() recursively and add the view names obtained from a RegexURLPattern object to a global list VIEW_NAMES.
from django.conf import settings
from django.core.urlresolvers import RegexURLResolver, RegexURLPattern
root_urlconf = __import__(settings.ROOT_URLCONF) # import root_urlconf module
all_urlpatterns = root_urlconf.urls.urlpatterns # project's urlpatterns
VIEW_NAMES = [] # maintain a global list
def get_all_view_names(urlpatterns):
global VIEW_NAMES
for pattern in urlpatterns:
if isinstance(pattern, RegexURLResolver):
get_all_view_names(pattern.url_patterns) # call this function recursively
elif isinstance(pattern, RegexURLPattern):
view_name = pattern.callback.func_name # get the view name
VIEW_NAMES.append(view_name) # add the view to the global list
return VIEW_NAMES
get_all_view_names(all_urlpatterns)
Getting list of all the views in a Django application:
To get the list of all the views present in a Django application, we will use the get_all_view_names() function defined above.
We will first import all the urlpatterns of the application and pass this list to the get_all_view_names() function.
from my_app.urls import urlpatterns as my_app_urlpatterns # import urlpatterns of the app
my_app_views = get_all_view_names(my_app_urlpatterns) # call the function with app's urlpatterns as the argument
my_app_views gives us the list of all the views present in my_app Django app.
Adding on to above fix by Rahul, if anyone is using Python3, you will need to use __name__ instead of func_name:
...
view_name = pattern.callback.__name__
...
otherwise you will get the following:
AttributeError: 'function' object has no attribute 'get_all_view_names'
(Thanks to scipy-gitbot at https://github.com/scipy/scipy/issues/2101#issuecomment-17027406
As an alternative, if you are disinclined to using global variables, here is what I ended up using :
all_urlpatterns = __import__(settings.ROOT_URLCONF).urls.urlpatterns
detail_views_list = []
def get_all_view_names(urlpatterns):
for pattern in urlpatterns:
if isinstance(pattern, RegexURLResolver):
get_all_view_names(pattern.url_patterns)
elif isinstance(pattern, RegexURLPattern):
detail_views_list.append(pattern.callback.__name__)
get_all_view_names(all_urlpatterns)
all_views_list = []
# remove redundant entries and specific ones we don't care about
for each in detail_views_list:
if each not in "serve add_view change_view changelist_view history_view delete_view RedirectView":
if each not in all_views_list:
all_views_list.append(each)
Then you can just iterate through all_views_list to get the list of filtered views.
update: Mar 1 2018
In Django 2.0, django.core.urlresolvers is moved to django.urls. RegexURLPattern and RegexURLResolver are renamed to URLPattern and URLResolver. So you should use
from django.urls import URLResolver, URLPattern
instead of
from django.core.urlresolvers import RegexURLResolver, RegexURLPattern
if you are using Django 2.
Get all Django and DRF views w/o using global vars
def get_all_views(urlpatterns, views=None):
views = views or {}
for pattern in urlpatterns:
if hasattr(pattern, 'url_patterns'):
get_all_views(pattern.url_patterns, views=views)
else:
if hasattr(pattern.callback, 'cls'):
view = pattern.callback.cls
elif hasattr(pattern.callback, 'view_class'):
view = pattern.callback.view_class
else:
view = pattern.callback
views[pattern.name] = view
return views
I'm writing a contest app. The contest closes at midnight on a particular date. I want to have the app automatically switch from: using a CookieWizardView, from formtools; to a normal TemplateView, from the generic view library.
Currently the relevant part of my urlpatterns looks like this:
urlpatterns += patterns('',
url(r'^$', 'appname.views.contest'), # the CookieWizardView
)
and I'd like it, after a certain date, to act as though it looks like this:
urlpatterns += patterns('',
url(r'^$', 'appname.views.contestclosed'), # a TemplateView
)
I am totally, totally fine with having a hard-coded magic number, I just don't want to be up at midnight that day!
~~
I solved this but can't answer my own question because I'm too new.
I made a function in my views.py:
def contest_switcher(request):
if datetime.datetime.now() < datetime.datetime(YEAR_OVER, MONTH_OVER, DAY_OVER):
return contest(request)
else:
return contestclosed(request)
This does the trick, now my urlpattern is:
urlpatterns += patterns('',
url(r'^$', 'appname.views.contest_switcher'),
)
I did have to add a function to my contest closed view, though, because it wasn't expecting a POST, which could happen if someone is trying to fill out the contest form at midnight:
class ContestClosedView(TemplateView):
template_name = "appname/closed.html"
def post(self, *args, **kwargs):
return self.get(*args, **kwargs)
contestclosed = ContestClosedView.as_view()
You don't have to try to hack your urls.py to pull this off. Set one URL pattern that points to a view that looks like this:
def contest_page(request, contest_id):
try:
contest = Contest.objects.get(pk=contest_id)
except Contest.DoesNotExist:
raise Http404 # minimum necessary - you can do better
if datetime.datetime.now() < contest.end_date: # model field rather than module constants
return contest(request, contest_id) # CookieWizardView
else:
return contestclosed(request, contest_id) # TemplateView
This is basically your contest_switcher with improvements:
Applies to multiple contests
Contests know their own end date so you don't clutter your module scope with constants
Simple urls.py and the view does the work of delegating what is shown (you know, the view)
(Note that this example implies that you would change your models correspondingly and import all the correct libraries and such.)