error when rendering through get_object_or_404 django - django

The following code in my views.py throws an error if I don't use
else conditional statement specifying detail = None
Is there any better way to simplify the code without using else statement?
(just in case you don't understand the structure of my app, it only has detail, and list templates. list.html only shows the list of name record in model, and detail only shows the each name in detail page.)
def SimpleView(request, sluggy=None):
s = Simple.objects.all()
if sluggy:
detail = get_object_or_404(Simple, slug=sluggy)
else:
detail=None
return render(request,'simple_app/list.html',{'s':s,'d':detail})
def detaily(request,sluggy):
sluggys = get_object_or_404(Simple,slug=sluggy)
return render(request, 'simple_app/detail.html',{'sluggy':sluggys})

You can build up the context directly:
def SimpleView(request, sluggy=None):
context = {'s': Simple.objects.all()}
if sluggy:
context['d'] = get_object_or_404(Simple, slug=sluggy)
return render(request,'simple_app/list.html', context)
(Though please use more descriptive names for the template vars; there's no reason to use single characters.)

If you are really fixated on no using the else part, you should define the detail variable before the if statement. So if "if qualification" is not satisfied, "detail" variable in return statement will at-least have a defined value as None.
def SimpleView(request, sluggy=None):
s = Simple.objects.all()
detail=None
if sluggy:
detail = get_object_or_404(Simple, slug=sluggy)
return render(request,'simple_app/list.html',{'s':s,'d':detail})
def detaily(request,sluggy):
sluggys = get_object_or_404(Simple,slug=sluggy)
return render(request, 'simple_app/detail.html',{'sluggy':sluggys})

Related

Get slug name list by url path name

I am reverse-engineering django urlpatterns. For my purpose, I need to dynamically know the list of slug names developers chose to use within url. (for weird decorator purposes outside of scope of this question)
Example:
path("<slug:membership_slug>/<slug:parent_slug>/data/", rzd.ReportingUnitDataView.as_view(), name="hierarchy.item.data"),
So, in the world, where everyone writes perfect code, my function whould take "hierarcy.item.data" and spit out ["membership_slug", "parent_slug"]
I looked at the reverse function, hoping it would return something useful without kwargs, but it does not. Have been googling like Neo, but the hope does not leave my heart... somewhere django should hold all that urlpatterns stuff! I still believe.
Perhaps you can at suggest how to get a hold of list of urls, even if in the raw format, that would already be a help. Thank you.
Search for book called Django 2x scopes this book hold every thing related to django best practices and of course the urls part.
This book will help you to understand more how django developers think.
Happy hacking 🙃
I think you're best guess is to write a Regex..
From what I've found in the source code they use _lazy_re_compile(r'<(?:(?P<converter>[^>:]+):)?(?P<parameter>[^>]+)>') which can be found in urls\resolvers.py in _route_to_regex
this is what I've come up with after butchering the source code
def geturlnamedarguments(route):
from django.utils.regex_helper import _lazy_re_compile
named_arguments = []
while True:
match = _lazy_re_compile(r'<(?:(?P<converter>[^>:]+):)?(?P<parameter>[^>]+)>').search(route)
if not match:
break
route = route[match.end():]
parameter = match['parameter']
named_arguments.append(parameter)
return named_arguments
You can also get the raw URL route as a string by doing
urlpatterns[index].pattern._route
You can find the URLPattern Object also in urls\resolvers.py if you want to play around with it.. I never found a place where they have the names in a raw format
Solved, It occurred to me, that I can just include urls.py and parse it out myself :)
from myapp import urls
class UrlPatterns:
#staticmethod
def _get_params(s):
items = []
is_exp = False
is_prefix = False
type = ''
name = ''
for n in range(len(s)):
if not is_exp:
if s[n] == '<':
is_exp = True
is_prefix = True
type = ''
elif is_prefix:
if s[n] == ':':
is_prefix = False
else:
type += s[n]
else:
if s[n] == '>':
is_exp = False
items.append({
'type': type,
'name': name
})
name = ''
else:
name += s[n]
return items
#staticmethod
def _find_pattern(url_name):
for url in urls.urlpatterns:
if url.name == url_name:
return str(url.pattern)
return None
#staticmethod
def get_required_params(url_name):
url_pattern = UrlPatterns._find_pattern(url_name)
if url_pattern:
return UrlPatterns._get_params(url_pattern)
return None

Django, how to reuse variable results get in a views in another one?

I have the following question for you. I want to know the best and elegant way to reuse some variable results obtained in a view in another one.
I'm trying to explain me better with an example.
I'have 2 differente app, app A and app B.
In app A views.py I have a lot of code to process data from the models with a creation of a lot of new variables.
Now, in app B I have the necessity to reuse some variables obtained in views.py app A, but I wonna write again the same code to get them.
Is there a way to achive my goal?
EDIT
Following the #sandeshdaundkar's suggestions I could overcome my problem creating a new function and call it in my views.py of each app.
I'm trying to achive this result but I'm not good at python's function and how call it inside my views.py.
I have created the following function in my_app/utility.py:
def my_func():
defaults = list(0 for m in range(13))
iva_debito = dict()
for year, month, totale in(Ricavi.objects.values_list( 'data_contabile__year', 'data_contabile__month').
annotate(totale=ExpressionWrapper(Sum(F('quantita') * F('ricavo')*(F('iva'))),
output_field=FloatField())).values_list('data_contabile__year', 'data_contabile__month', 'totale')):
if id not in iva_debito.keys():
iva_debito[id]=list(defaults)
index=month
iva_debito[id][index]=totale
iva_debito_totale={'IVA a Debito Totale': [sum(t) for t in zip(*iva_debito.values())],}
context= {
'iva_debito_totale':iva_debito_totale,
}
return context
And here my my_app/views.py:
...
from .utility import my_func
def iva(request):
data = my_func()
iva_debito_totale=data['iva_debito_totale']
context= {
'iva_debito_totale': iva_debito_totale,
}
return render(request, 'iva/iva.html', context)
I have solved it with the solution above.
def func_name():
# do calculation,
return context_for_view_one, obj_to_reuse
def view_one(request):
context, iva = func_name()
return render(context=context) # context to be used in view a
def view_two(request):
context, iva = func_name()
return render(context=iva) # iva to be used in view b
def iva(request):
data = func_name()
iva_debito_totale = data['iva_debito_totale']
return render(request, 'template.html', context={'totale': iva_debito_totale})
I hope this gives you an idea, we return 2 objects from common function. One which is to be used in view_one and others to be used in view_two. You can try something similiar

Django not displaying correct URL after reverse

There's lots of documentation about Django and the reverse() method. I can't seem to locate my exact problem. Suppose I have two urlconfs like this:
url(r'ParentLists/$', main_page, name = "main_page"),
url(r'ParentLists/(?P<grade_level>.+)/$', foo, name = "foo")
and the two corresponding views like this:
def main_page(request):
if request.method == 'POST':
grade_level = request.POST['grade_picked']
return HttpResponseRedirect(reverse('foo', args = (grade_level,)))
else:
return render(request, 'index.html', context = {'grade_list' : grade_list})
def foo(request, grade_level):
grade_level = request.POST['grade_picked']
parent_list = # get stuff from database
emails = # get stuff from database
return render(request, 'list.html', context = {'grade_list' : grade_list, 'parent_list' : parent_list})
Here, list.html just extends my base template index.html, which contains a drop down box with grade levels. When the user goes to /ParentLists, the main_page view renders index.html with the drop down box as it should.
When the user picks a grade level from the drop down box (say 5th Grade), the template does a form submit, and main_page once again executes - but this time the POST branch runs and the HttpResponseRedirect takes the user to /ParentLists/05. This simply results in an HTML table pertaining to grade 5 being displayed below the drop down box.
The problem is, when the user now selects say 10th Grade, the table updates to show the grade 10 content, but the URL displayed is still /ParentLists/05. I want it to be /ParentLists/10.
Clearly, after the first selection, the main_page view never executes again. Only foo does...and so the HttpResponseRedirect never gets called. How should I reorganize this to get what I'm looking for? Thanks in advance!
As you correctly mentioned you will never redirect to foo() from foo().
So the simple way to fix this is just add similar code as in main_page() view:
def foo(request, grade_level):
if request.method == 'POST':
grade_level = request.POST['grade_picked']
return HttpResponseRedirect(reverse('foo', args = (grade_level,)))
else:
parent_list = # get stuff from database
emails = # get stuff from database
return render(request, 'list.html', context = {'grade_list' : grade_list, 'parent_list' : parent_list})
Please note that I remove grade_level = request.POST['grade_picked'] because as Nagkumar Arkalgud correctly said it is excessively.
Also instead of combination of HttpResponseRedirect and reverse you can use shortcut redirect which probably little easy to code:
from django.shortcuts redirect
...
return redirect('foo', grade_level=grade_level)
I would suggest you to use kwargs instead of args.
The right way to use the view is:
your_url = reverse("<view_name>", kwargs={"<key>": "<value>"})
Ex:
return HttpResponseRedirect(reverse('foo', kwargs={"grade_level": grade_level}))
Also, you are sending "grade_level" to your view foo using the URL and not a POST value. I would remove the line:
grade_level = request.POST['grade_picked']
as you will override the grade_level sent to the method from the url.

using two templates from one view

I am trying to present content from a view in two ways: html and csv download. The only way I was able to do it was to use 2 different views, one for html presentation and one for csv. This duplicates my code and I am looking for a more elegant solution.
Any suggestions?
Here is the sample code:
# views.py
[...]
def member_list(request):
member_list = Member.objects.all()
return render_to_response("member_list.html",
{'member_list':member_list)
def member_csv_list(request):
member_list = Member.objects.all()
csv_list = HttpResponse(content_type='text/csv')
csv_list['Content-Disposition'] = 'attachment; filename="member_list.csv"'
writer = csv.writer(csv_list)
writer.writerow(['Name', 'Member Type', 'Rooms'])
for member in member_list:
fields = [member.name, member.member_type, member.room]
writer.writerow(fields)
return member_list
You can use a parameter in your url and implement a view like
def myview(request) :
type = request.GET.get('type', 'html')
# do processing
if type == 'html':
# return html
else if type == 'csv':
# return csv
If you access a url like http://yourserver/myview?type=csv it will render the csv part of the view. When the url http://yourserver/myview is accessed it will return the html part of the view.
Rohan's answer is absolutely the right paradigm. For an excellent tutorial-style introduction to this topic, cf. Multiple Templates in Django.
Here are a few quotes (all credit goes to Scott Newman).
To serve a printable version of an article, for example, we can add ?printable to the end of the URL.
To make it work, we'll add an extra step in our view to check the URL for this variable. If it exists, we'll load up a printer-friendly template file. If it doesn't exist, we'll load the normal template file.
def detail(request, pid):
'''
Accepts a press release ID and returns the detail page
'''
p = get_object_or_404(PressRelease, id=pid)
if request.GET.has_key('printable'):
template_file = 'press/detail_printable.html'
else:
template_file = 'press/detail.html'
t = loader.get_template(template_file)
c = Context({'press': p})
return HttpResponse(t.render(c))
He continues with template overrides and different templates by domain names. All this is excellent.

Django - Access Context Dictionary Before Template

I'm hoping to use a context processor or middleware to modify the values of the dictionary passed to render_to_response prior to the actual rendering. I have a messaging schema I'm trying to implement that would populate a message list based on the existence of a type of user that I'd like to search the context for prior to the rendering of the template.
Example:
def myview(...):
...
return render_to_response('template.html',
{'variable': variable},
)
and I'd like to be able to add additional information to the context on the existence of 'variable'.
How can I access 'variable' after my view defines it but before it gets to the template so that I can further modify the context?
use TemplateResponse:
from django.template.response import TemplateResponse
def myview(...):
...
return TemplateResponse(request, 'template.html',
{'variable': variable},
)
def my_view_wrapper(...):
response = my_view(...)
variable = response.context_data['variable']
if variable == 'foo':
response.context_data['variable_is_foo'] = True
return response
This is easy. If you have supplied just a little bit more code in your example the answer might have bit you.
# first build your context, including all of the context_processors in your settings.py
context = RequestContext(request, <some dict values>)
# do something with your Context here
return render_to_response('template.html', context)
Update to comment:
The result of a render_to_response() is an HTTPResponse object containing a template rendered against a Context. That object does not (to my knowledge) have a context associated with it. I suppose you could save the result of render_to_response() in a variable and then access the Context you passed it, but I'm not sure what problem you are trying to solve.
Did you modify the Context during rendering? If so you may find that the information is not there any longer because the Context has a scope stack which is pushed/popped during template processing.
You can create a dictonary for the context:
def myview(...):
c = dict()
c["variable"] = value
...
do some stuff
...
return render_to_response('template.html',c)
Maybe the RequestContext is the thing you are looking for.