Django Admin Custom Pages and Features - python

I'm (new to) working in Django and would like to create two features that do not rely upon models/database tables. The basis of this app is as a web-based wrapper to a Python application.
The features are:
I would like to be able to load a ConfigObj text file into a page and edit it's configuration prior to saving again.
I would like to be able to call command line python/bash scripts and display their output on a page - like exec in PHP.
At the moment I'm working on simple custom admin pages without model as described here:
Django admin, section without "model"?
Would this be the right direction to go in? I'm not sure proxy tables apply as the features I desire have nothing to do with any data.
So far I have looked at is it possible to create a custom admin view without a model behind it and a few other links. At the moment I have:
main/urls.py
url(r'^admin/weectrl', include('weectrl.urls')),
which links with weectrl/urls.py
from weectrl import views
urlpatterns = patterns('',
(r'^admin/weectrl/manage/$', weectrl_manage_view),
(r'^admin/weectrl/config/$', weectrl_config_view),
)
which points to weectrl/views.py
def weectrl_manage_view(request):
r = render_to_response('admin/weectrl/manage.html', context, RequestContext(request))
return HttpResponse(r)
def weectrl_config_view(request):
r = render_to_response('admin/weectrl/config.html', context, RequestContext(request))
return HttpResponse(r)
The current error message is name 'weectrl_manage_view' is not defined

Ok, found something that works.
In the main url.py
url(r'^admin/weectrl/', include('weectrl.urls')),
In app/urls.py
urlpatterns = patterns('',
url(r'^config/$', views.config, name='config'),
url(r'^manage/$', views.manage, name='manage'),
)
and in app/views.py
def config(request):
context = ""
return render(request, 'weectrl/config.html', context)
def manage(request):
context = ""
return render(request, 'weectrl/manage.html', context)
html files are in app/templates/app/...

Related

Customize django admin. Problem with no-model views

Im trying to create a extra view in django admin, on the left navbar. This view will be responsible for uploading a file, which will be parsed in some function (in future i would like to render result of this parsing in admin page). This file wont be saved in database, so there wont be a model. Is there any possibility to add a view to django admin (left navbar) which dont have a model? I was reading a lot, and could find a solution. What i have done for now:
Created a class which inherits from AdminSite. I tried to implement get_app_list method, but variable self._build_app_dict(request) was empty array, and this means, method couldn't find a installed aps. I wanted to add new object to app_list variable, to render it on website.
Tried to override a admin templates, but couldnt render it. I tried to override app_index.html which i put on folder: app_name/templates/admin/app_index.html
Here is my code, which ofc doesnt work:
class MyCustomAdmin(AdminSite):
def get_app_list(self, request):
"""
Return a sorted list of all the installed apps that have been
registered in this site.
"""
app_dict = self._build_app_dict(request)
breakpoint()
app_list = sorted(app_dict.values(), key=lambda x: x['name'].lower())
for app in app_list:
app['models'].sort(key=lambda x: x['name'])
return app_list
def get_urls(self):
from django.conf.urls import url
urls = super(MyCustomAdmin, self).get_urls()
urls += [
url(r'^my_custom_view/$', self.admin_view(MyCustomView.as_view()))
]
return urls
class MyCustomView(View):
template_name = 'admin/app_index.html'
def get(self, request):
print('fefef')
return render(request, self.template_name, {})
def post(self, request):
pass
admin_site = MyCustomAdmin()
admin_site.get_app_list(AdminSite.get_app_list)

Not understanding how Python Class works

I am in need of some help understanding how Python and Django work based on some code I'm looking at.
Say my urls.py file has the following
router.register(r'testing', SomeClass)
and then in my views.py file, it is set up like this:
class SomeClass():
database = DatabaseValues.objects.all()
def first_def(self):
# do some filtering and such on db results
return database
def second_def(self):
a = 20
b = 40
return b - a
def third_def(self):
z = 200
y = 400
return y - z
When the SomeClass is called in UI by hitting the http://localhost/testing url, what is returned??
I would recommend making some changes to urls.py I think this method below is easier to understand.
when you run python3 manage.py runserver
you can open 127.0.0.1/example.
the reason you are not understanding is because Django has a lot of boilerplate code. I would recommend reading more into how views work.
every class in views.py should inherit a django class in this example I use TemplateView. your app name is the folder that is created when a new django "app" is added to the project.
from django.urls import path
from .views import exampleView
app_name = "example"
urlpatterns = [
path('/example', exampleView.as_view(), name='example'),
]
in views.py you cannot create arbitrary function names. when a website loads
www.baseurl/example you are making a get request. therefore you must have a get method inside of your class.
template_name refers to an html page that will be rendered...
this template name must point at an html file which is located inside project_root/templates/html_file.html
views.py
from django.views.generic import TemplateView, ListView
class exampleView(TemplateView):
template_name = 'test.html'
def get(self, request):
print("hello")
return render(request, self.template_name, {})
if you wish to have arbitrary functions inside your class cool. Add them below the get function and call them when a user makes a get request.

Passing context to django-registration's views

I'm utilizing django-registration with a set of premade templates I found on Github for doing a two-step (registration-activation) workflow using HMAC.
I want to pass global variables (defined in context-processors) like my website's name to the emails sent by django-registration. the activation email sent to a new registrant, for example, or the password change one.
The "problem" is I don't directly have access to those views. That's kinda the point of django-registration, you include its path in the urls.py file, and everything works:
urlpatterns = [
url(r'^', include('core.urls')),
url(r'^admin/', admin.site.urls),
url(r'^accounts/', include('registration.backends.hmac.urls')),
]
What's the minimum effort way of adding context to those views? I've already created and am successfully passing context to emails in my own views (using context processors):
def send_some_email_view(request):
msg_plain = render_to_string('email_change_email.txt', context, request=request)
msg_html = render_to_string('email_change_email.html', context, request=request)
But what about views I didn't create?
Edit: So I made some progress, finding django-registration's registration view, and this method inside of it:
def send_activation_email(self, user):
"""
Send the activation email. The activation key is simply the
username, signed using TimestampSigner.
"""
activation_key = self.get_activation_key(user)
context = self.get_email_context(activation_key)
context.update({
'user': user
})
subject = render_to_string(self.email_subject_template,
context)
# Force subject to a single line to avoid header-injection
# issues.
subject = ''.join(subject.splitlines())
message = render_to_string(self.email_body_template,
context)
user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
I don't want to override it inside the source code because that would prevent me from updating. So now the question becomes: Is my only way out writing a view that subclasses this view, and overriding the method? This means I'm writing separate views for every view provided by django-registartion that needs to send an email...
First create your own view based on the vendor view and overwrite your desired method:
from registration.backends.hmac.views import RegistrationView
class CustomRegistrationView(RegistrationView):
def get_email_context(self, user):
context = super().get_email_context(user)
return RequestContext(self.request, context)
Than have a look at registration.backends.hmac.urls.py (source). They just define a bunch of urls there.
You can easily overwrite just one of them by adding your own before you include the one from the app.
from yourapp import views
urlpatterns = [
# [...]
url(r'^accounts/register/$', views.CustomRegistrationView.as_view(), name='registration_register'),
url(r'^accounts/', include('registration.backends.hmac.urls')),
# [...]
]
While changing just the necessary things you also get some insight of whats going on in your 3rd party app, which is always an advantage. This applies to most 3rd party apps not just the one you are currently using.
Here's what I ended up doing, Thanks to the direction dahrens' answer sent me to:
# myapp/processors.py
def get_website_name(request):
website_name = 'ExcitingWebsiteThatsComingSoon'
return {'mysite_name': website_name}
# some views.py file
from myapp.processors import get_website_name
class RegistrationViewWithContext(RegistrationView):
def get_email_context(self, user):
context = super().get_email_context(user)
context['req'] = get_website_name(self.request)
return context
Basically, I'm simply using my custom processor to inject the website's name. It isn't as clean as I hoped it would be: While in my templates I can simply use {{ mysite_name}}, in the email template I have to use {{req.mysite_name}}. But this does have the DRY-ness I aimed for: all templates updating accordingly if the variable in the function changes.
I'll mark my answer as correct for now and will update accordingly if any new answers come in.

Django Admin Custom View URL not being recognized

I am using Django 1.8 on Python 3.4.3
I have a Model called FormUpload and I am adding a ModelAdmin for the same in admins.py in the same application "mca".
#admin.register(FormUpload)
class FormUploadAdmin(admin.ModelAdmin):
def upload_form(self, request):
context = dict(
# Include common variables for rendering the admin template.
self.admin_site.each_context(request),
# Anything else we want in the context...
)
return TemplateResponse(request, "admin/formupload.html", context)
def get_urls(self):
urls = super(FormUploadAdmin, self).get_urls()
upload_admin_urls = [
url(r'^upload/$', self.admin_site.admin_view(self.upload_form)),
]
logger.debug(upload_admin_urls)
return upload_admin_urls + urls
This should have been available at /admin/mca/upload/ . However when I go the URL I get a 404 saying the current URL doesn't match anything
Here is the output of the debug
RegexURLPattern None ^upload/$
(Removed few things that will make the display weird for the debug).
Please notice the None. If the other URLs are listed out there is a method name where the None is.
What am I doing wrong here ? I am following the approach suggested in django documentation - https://docs.djangoproject.com/en/1.8/ref/contrib/admin/#modeladmin-methods

passing data between class based forms

I am fairly new to Django and class based forms, and I am having trouble understanding how these interact with each other. Following from the django project example, I have tried to build a "search form", which would sit on all pages of my project:
# forms.py
from django import forms
class SearchForm(forms.Form):
myquery = forms.CharField(max_length=255,label="", help_text="sq")
def __unicode__(self):
return self.myquery
# views.py
from searchapp.forms import SearchForm
from django.views.generic.edit import FormView
from django.views.generic import TemplateView
class SearchView(FormView):
template_name = 'index.html'
form_class = SearchForm
success_url = '/searchres/'
def form_valid(self, form):
thequery=form.cleaned_data.get('myquery')
return super(SearchView, self).form_valid(form)
class Meta:
abstract = True
class SearchResView(SearchView):
template_name = 'searchres.html'
#urls.py
from django.conf.urls import patterns, include, url
from django.conf import settings
from deals.views import IndexView
from searchapp.views import SearchView, SearchResView
urlpatterns = patterns('',
url(r'^index/', SearchView.as_view(),name="home"),
url(r'^searchres/', SearchResView.as_view(),name="searchresx"),
)
The plan is the start off with a simple form for user to enter the search query, and also show the input form on the results page. I have the following questions here (sorry - I am a Django newbie esp. to Class Based Views):
How does one pass data ("thequery") to the success_url? i.e I would like success_url to have access to "thequery" so that I can use something like {{thequery}} on my template tags.
Upon submitting the form(name="home"), I see POST data from the form on my firebug, but I am able to see just "myquery" rather than "thequery". How does one use get_context_data() here to add/post "thequery" variable aswell?
Finally, I was wondering if it would be possible to construct the success_url based on "thequery" string i.e something like success_url = '/searchres/?q=' + thequery
Thank you in advance - I am hoping to learn more.
I would suggest using function based views for this. If you choose to subclass a generic view you will need to dig through a lot of documentation and possibly source code, to find the right methods to override. (If you're really keen then look at the ListView class along with the get_queryset(), get() and post() methods)
A single django view will normally handle both rendering the empty form AND processing the submitted form.
So the search page (both the form and the results), live at http://your-site.com/search. Your url conf is -
urlpatterns = patterns('',
#...
(r'^search/$', 'searchapp.views.search'),
)
And your view looks something like this -
def search(request):
if request.method == 'POST':
form = SearchForm(request.POST)
if form.is_valid():
my_query = form.cleaned_data['myquery']
object_list = YourModel.objects.filter(# some operation involving my_query)
return render_to_response('search_results.html', {'object_list': object_list})
else:
form = SearchForm()
render_to_response('search_form.html', {'form': form})
(Note I've assumed your form method is post rather than get - I know this isn't great http but it's a common pattern with django)
To respond to your questions -
Don't use your own method for cleaning data. Add a clean_myquery method to your form and access it with form.fields['myquery'].clean() (or if you've called is_valid() on your form, it's accessible with just form.cleaned_data['myquery']).
You want to try and avoid passing data for processing to the template. Do as much processing as you can in the view, then render the template. However if you want to pass myquery as a string for the template to render, then add it in to the context dictionary (the second non-key-word argument) in render_to_response -
return render_to_response('search.html', {'object_list': object_list, 'myquery': my query})
The post data is constructed from the form fields. You don't have a form field thequery. The view is processing the POST data - it's not creating it that's done by the html (which in turn is constructed by the Form class). Your variable thequery is declared in the view.
Django's URL dispatcher ignores query strings in the URL so http://your_site.com/ssearch will be processed by the same view as http://your_site.com/search?myquery=findstuff. Simply change the html in the template from <form method='post'> to and access the data in django with request.GET. (You'll need to change the code from the view I described above to include a new check to see whether you're dealing with a form submission or just rendering a blank form)
Have a good read of the docs on views, forms and the url dispatcher.

Categories