Can I change the template search behavior of class based views? - python

I have an app called transactions. Within this app I have a model called BatchFile. In my views.py file, I subclass ListView (amongst others). The default behavior is that django thinks batchfile_list.html should be located at:
templates/transactions/batchfile_list.html
That's great, but the folder is getting crowded. I'm able to add "templates/transactions/batchfiles" to TEMPLATE_DIRS, but because the default behavior is to look for appname/modelname_type.html that requires that I put my templates in:
templates/transactions/batchfiles/transactions/batchfile_list.html
when I'd really like it to be in:
templates/transactions/batchfiles/batchfile_list.html
or my optimal result:
templates/transactions/batchfiles/list.html
Are there configuration options that would allow me to do this? I know the optimal result is probably not cleanly achieved, but I was hoping for at least the slightly less optimal result.
Thanks!

The easiest way is to specify template_name:
class MyListView(ListView):
template_name = "transactions/batchfiles/list.html"
An alternative is to override get_template_names:
class MyListView(ListView):
def get_template_names(self):
return ["transactions/batchfiles/list.html"]
Note that it needs to return a list of possible template locations.
In this case there would only be one which is the desired location.

Related

Django-Viewflow, customize archive view

I am trying to customize the default /workflow/archive/ view from Viewflow.
(as in http://demo.viewflow.io/workflow/archive/)
I need to remove some of the columns and add some additional ones specific to my task.
I can't seem to find an easy way. I have been digging and have found that I can try to override the class viewflow.frontend.views.AllArchiveListView and adding the mapping
url('^test/', AllArchiveListView.as_view())
but I get no data when doing that (seems like the flow_classes internal parameter is empty when it gets called like that)
I have also tried to create the viewflow/site_archive.html template but seems overkill.
Any ideas that may point me in the right direction would be highly appreciated!
To customize a common frontend list view, you need to replace viewflow.frontend with you own frontend application. To simplify development, you can inherit from viewflow frontend base classes
# apps.py
from django.apps import AppConfig
from viewflow.frontend.apps import ViewflowFrontendConfig
class FrontendConfig(ViewflowFrontendConfig):
viewset = 'frontend.viewset.FrontendViewSet'
def register(self, flow_class, viewset_class=None):
super().register(flow_class, viewset_class=viewset_class)
# views.py
from viewflow.frontend.views import AllTaskListView as BaseAllTaskListView
class AllTaskListView(BaseAllTaskListView):
list_display = [
'task_hash', 'description'
]
# viewset.py
from viewflow.frontend.viewset import FrontendViewSet as BaseFrontendViewSet
class FrontendViewSet(BaseFrontendViewSet):
inbox_view_class = views.AllTaskListView

Django: correct use of class-based View inheritance?

I would like almost every View in my Django project to compute, for instance, time_on_mars. This variable needs to be put into every template context, so every template can draw time_on_mars. I'd also like every View to be able to use that variable if it wishes, maybe to compute is_it_nighttime_on_mars or whatever. I'd like to do this using inheritance, or some other mechanism, so when I provision a whole lot more than mars time, I don't repeat myself.
Is this an appropriate use of class-based Views? I guess I'd create a base class view, and in its get and post methods, calculate the current time on mars, then call child class methods 'doGet' or 'doPost', intercept the result, put the mars time into the result, and continue. Seems tedious.
Decorators? Is there a way I can put time_on_mars into the closure context for the view?
What's the pythonic (djangonic?) way to do this?
EDIT: I like the idea of context processors, but I've changed my mind - instead of every view, I'd like most views to get this. Especially if it's expensive to compute the time on mars...
EDIT 2: Problem description not adequate! To clarify:
I have a bunch of views that do this:
def some_view(request):
w,x,y,z = provision_wxyz()
... some biz logic, maybe compute a,b,c using w,x,y,z ...
return render(request, 'some_template.html', { 'a':a, 'b':b, 'c':c, 'w':w, 'x':x, 'y':y, 'z':z })
... and I'd like to abstract out the first step (w,x,y,z=), and the adding of w,x,y,z to the template context. Context processors don't work, because inside the some_view business logic, I want access to w,x,y,z. I don't like doing the mixing strategy for CLA on the TemplateView, because again, it doesn't give me access to w,x,y and z inside some_view (or whatever the equivalent of some_view is), and I want to, well, do a bunch of business logic somewhere, and TemplateView doesn't seem to give me that?
You can definitely use CBV's for this. It is a great use for Mixins which take advantage of pythons multiple inheritance. Here is a quick and dirty example I just wrote out.
class MarsMixin(object):
time_on_mars = 5
def get_time_on_mars(self):
"""
Does what it takes to return a time on mars be it calculation
or returning a property set on the object. Should return a property
from the object if it is a constant. Should calcualte in the method
if it is going to be dynanic
"""
return self.time_on_mars
def get_context_data(self, **kwargs):
context = super(MarsMixin, self).get_context_data(**kwargs)
context['time_on_mars'] = self.get_time_on_mars()
return context
class HomeView(MarsMixin, TemplateView):
template_name = 'home/index.html'
Biggest notes are the mixin inherits from object. The other is you inherit from mixin in the HomeView and it is listed before the TemplateView.
I think you can still use context processors, the tip that can help is to prefix the urls where you want to do your calculation, for example they will start by '/mars/...'. You can also use another kind of prefix.
Like this you can check on the context processor method:
def go_to_mars(request):
if request.path.startswith('/mars/'):
return {'time_on_mars': calculate_time_on_mars()}
return {}

Ways of defining a field in Django form

I am defining a field in a django form in the following two ways :
class MyForm(forms.Form):
myfield = forms.ChoiceField(choices=[(u.id,u.username) for u in User.objects.filter(type="TYPE1")])
OR
class MyForm(forms.Form):
pass
def_init_(self,*args,**kwargs):
super(MyForm,self)._init_(*args,**kwargs)
self.fields['myfield'] = forms.ChoiceField(choices=[(u.id,u.username) for u in User.objects.filter(type="TYPE1")])
Is there any different between these two approaches?? I tried to find this on web but did not get any relevant answers.
First of all, there is a special field to handle such things - ModelChoiceField
In your example the difference is the moment when code is executed.
In first approach it is executed when module with form is loaded, in second - every time the form is initialized (so basically on each request to your view). So first approach has a problem - Users will be loaded on first request. If any user registers after this moment - he will not be present in the select field until you restart the server.
Also I think it is a bad practice to introduce new field in __init__ method. If you really need something like this and you can't use ModelChoiceField the better way is
class MyForm(forms.Form):
myfield = forms.ChoiceField()
def __init__(self,*args,**kwargs):
super(MyForm,self).__init__(*args,**kwargs)
self.fields['myfield'].choices = [(u.id, u.username) for u in User.objects.filter(type="TYPE1")]
Don't you get an error when you run your server using the first way?
class MyForm(forms.Form):
myfield = forms.ChoiceField(choices=[(u.id,u.username) for u in User.objects.filter(type="TYPE1")])
If you use this method That for will be executed when the file is being read. Try using the second way or a function to execute that for and set the choices.
Usually in Form.__init__ you define some dynamic fields (for example, you want to show some checkboxes, which are set/unset according to the data from some model). In all other cases first approach is better because it's more readable.

How to specify label_attr for a model in a Flask-Admin ModelView using MongoEngine?

I think I have a pretty common use case and am surprised at how much trouble it's giving me.
I want to use a key-value pair for a ReferenceField in the Flask-Admin edit form generated by the following two classes:
class Communique(db.Document):
users = db.ListField(db.ReferenceField(User), default=[])
class User(db.Document):
email = db.StringField(max_length=255, required=True)
def __unicode__(self):
return '%s' % self.id
I want the select to be constructed out of the ObjectId and the an email field in my model.
By mapping the __unicode__
attribute to the id field I get nice things on the mongoengine side like using the entire object in queries:
UserInformation.objects(user=current_user)
This has the unfortunate effect of causing the Flask-Admin form to display the mongo ObjectId in the edit form like so:
The docs say I have to provide the label_attr to the ModelSelectMultipleField created by Flask-Admin. I've done so by overriding the get_form method on my ModelView:
def get_form(self):
form = super(ModelView, self).get_form()
form.users = ModelSelectMultipleField(model=User,
label_attr='email',
widget=form.users.__dict__['kwargs']['widget'])
return form
I'm reusing the the widget used by the original form.users (which may be wrong). It works fine when editing an existing item, BUT throws an exception when creating a new one (perhaps because I'm reusing the widget).
All of this seems like way more work than should be needed to simply provide a label_attr to my SelectField. Fixing up the listing view was a simple matter of adding an entry to the column_formatters dictionary. Is there no simple way to specify the label_attr when creating my ModelView class?
I know I could make this problem go away by returning the email property in the __unicode__ attribute, but I feel like I shouldn't have to do that! Am I missing something?
Oy, now I see how to do it, though it's not that obvious from the docs. form_args is a dictionary with items keyed to the form models. All I needed to do was...
form_args = dict(users=dict(label_attr='email'))
Which does seem about the right amount of effort (considering Flask-Admin isn't some sort of java framework).

Display icons in Django admin for each item

I want to add a specified icon for each model on admin index page. I added an attribute named "picture" on each model then I modified /contrib/admin/sites.py to pass that picture name to template and checked and use it on index.html template of admin to get the result.
I wonder to know if there is a better way
class Product(models.Model):
abbr = models.CharField(max_length=20,unique=True)
title = models.CharField(max_length=200,unique=True)
owner = models.ForeignKey(UserProxy)
des = models.TextField(blank=True,null=True)
picture = 'product.png'
def __unicode__(self):
return self.abbr
class Meta:
none
What You did seems OK, only small tips that can make Your code a little better:
Instead of modifying django/contrib/admin/sites.py You can subclass the AdminSite class (if You didn't do that already).
Modify the AdminSite.index() method to pass not picture, but whole admin class (there is a model_admin variable available in the index() method).
Assign picture in the ModelAdmin classes, not models, to separate admin stuff from models.
The answer from 'Python Fanboy' was so brief but useful to me, I could avoid modifying django base classes
picture field were moved to admin class
I subclass AdminSite as CustomAdminSite and copied index and app_index and made modification
(I don't know if there is a better way than copying whole of index and app_index like overriding)
In urls.py, 'admin.sites.url' replaced by 'custom_site.url'
(custom_site is instance of CustomAdminSite)
I did not want another url instead of '/admin' like '/my_admin' so I have to use instance of CustomAdminSite for all my models even User, Group & Site
I'm using admin_tools, now I've lose my application menu
Any better Idea or solution for my new encountered problems?

Categories

Resources