Customize the styles of Django ClearableFileInput widget - python

I have use ImageField in my django model to have the image upload facility. ImageField uses the ClearableFileInput widget but it does not provide well formatted html markup that i can customize using CSS. Shown bellow is the html markup that rendered by ClearableFileInput.
<div class="form-group" id="div_id">
<label class="control-label " for="id_image">
Guide
</label>
<div class="controls ">
Currently:
de.png
<input type="checkbox" name="image_" id="image_">
<label for="image_te">
Clear
</label><br>
Change:
<input type="file" name="image_te" id="id_i" class="clearablefileinput">
</div>
</div>
Simply what i want to do is to add custom css classes to these elements and change the order as i want. It would be really great if someone can suggest me a solution to this.

Just make your own Input class and alter the render callable to whatever you want. As an example, here's one I made to add in a little avatar. It's quick and dirty, in that it's not DRY, but it serves a purpose:
class AvatarInput(ClearableFileInput):
'''renders the input file as an avatar image, and removes the 'currently' html'''
template_with_initial = u'%(initial)s %(clear_template)s<br />%(input_text)s: %(input)s'
def render(self, name, value, attrs=None):
substitutions = {
'input_text': self.input_text,
'clear_template': '',
'clear_checkbox_label': self.clear_checkbox_label,
}
template = u'%(input)s'
substitutions['input'] = super(AvatarInput, self).render(name, value, attrs)
if value and hasattr(value, "url"):
template = self.template_with_initial
substitutions['initial'] = (u'<img src="%s" width="60" height="60"></img>'
% (escape(value.url)))
if not self.is_required:
checkbox_name = self.clear_checkbox_name(name)
checkbox_id = self.clear_checkbox_id(checkbox_name)
substitutions['clear_checkbox_name'] = conditional_escape(checkbox_name)
substitutions['clear_checkbox_id'] = conditional_escape(checkbox_id)
substitutions['clear'] = CheckboxInput().render(checkbox_name, False, attrs={'id': checkbox_id})
substitutions['clear_template'] = self.template_with_clear % substitutions
return mark_safe(template % substitutions)
Then just drop it into your form class Meta:
class Meta:
model = <your model>
widgets = {'your-field-name': AvatarInput()

Related

Django removes my custom html attributes from option tag

I am trying to add custom data-* attributes to the option tag in a select element. I have my custom template (widget) which is used by Django, but it seems like Django removes my custom attributes. somewhere in further steps
My custom option template:
widgets/tree_option_template.html
<option value="{{ widget.attrs.lft|stringformat:'s' }}"
data-test="test"
>{{ widget.label }} - {{ widget.attrs.rght|stringformat:'s' }}</option>
Custom widget:
class MultiChoiceFilterWidget(forms.SelectMultiple):
"""
TODO.
Based on FilteredSelectMultiple
"""
option_inherits_attrs = True
option_template_name = "widgets/tree_option_template.html"
...
Usage in admin.py:
class UserAdminForm(forms.ModelForm):
class Meta:
model = User
fields = "__all__"
read_projects = CustomTreeNodeMultipleChoiceField(
queryset=Project.objects.filter(disabled=False),
required=False,
widget=MultiChoiceFilterWidget(verbose_name="Projects", is_stacked=False),
)
When I am changing e.g. value attribute then it changes in DOM as well but my custom attributes are not available in HTML:
As we can see there is no data-test attribute...
Any idea why my custom tags are not visible in HTML?
Thanks!
ANSWER/SOLUTION
I found the problem. The problem was that the FilteredSelectMultiple after which I inherit has attached some JS files which were manipulating my HTML attributes.
#property
def media(self):
extra = "" if settings.DEBUG else ".min"
js = [
"vendor/jquery/jquery%s.js" % extra,
"jquery.init.js",
"core.js",
"SelectBox.js",
"SelectFilter2.js",
]
...
When I removed these JS files (SelectBox.js precisely) then my custom HTML attributes appeared.

ValueError could not convert string to float: ''

I'm having my app on heroku.
I use timeme js to record user's active time spend on the page, and use a hidden form to store the value into the database. Additionally, I'm using otree package to write my app.
The following is the code I use:
models.py
class Active(ExtraModel):
active_duration = models.FloatField()
active_record = models.LongStringField()
views.py/pages.py in otree
class intro_instructions(Page):
def before_next_page(self):
data = self.form.data
p = self.player
active = Active.objects.create(active_duration=data['active_duration'],
active_record=data['active_record'])
active.save()
html
<form method="post" role="form" id="form" autocomplete="off">{% csrf_token %}
<input type="text" name="active_record" id="id_active_record" style="display: none" >
<input type="number" step='any' name="active_duration" id="id_active_duration" style="display: none">
</form>
The error
ValueError
could not convert string to float: ''
{
"csrfmiddlewaretoken": "vVhVRLrAUokmiNGRpzCaP78bnTOQyowf5VMfIDgKGglWGuEyQAU2wooWjQzXuBgD",
"active_duration": "",
"active_record": ""
}
Is it because active_duration is empty? Will it help if I set blank=true, null=true for the forms?
I was assuming that every use would have value for the input. Also any ideas about why the input is empty? Could it be that the user used scripts to skip the page without visible fields? From the sentry error message, this happened to one user twice.
It is because you cannot cast an empty string to a float. You can do this instead though:
active_duration=data.get('active_duration') or "0"
This will give you a "0" if data["active_duration"] is empty, False, or None.
>>> data = {"foo": ""}
>>> data.get("foo") or "bar"
'bar'

Delete a django model object with blank file field

I have a django model that looks like this
class Foo(models.Model):
...
article = models.FileField(upload_to='articles', blank=True, default=None, null=True)
...
when I try to delete an object bar of Foo model using bar.delete() where any file has not been uploaded in the article field, I get the following error.
[Error2] Is a directory
How can I delete such objects?
EDIT
The code for the deletion looks like this:
HTML
<div class="form-group">
<input type="radio" name="delete" value="yes" /> Delete
<input type="radio" name="delete" value="no" checked="checked" /> Don't Delete
</div>
DJANGO
def del_foo(request,foo_id):
context_dict = {'msg':'Unable to delete'}
try:
bar = models.Foo.objects.get(id=foo_id)
_delete = True if request.POST.get("delete")=="yes" else False
if _delete:
bar.delete()
return HttpResponseRedirect('/home')
except Exception as e: print str(e)
return render(request, 'edit.html', context_dict)
You haven't included the relevant code for the delete operation, but it sounds like you calling os.remove(path). Since the filename is blank the path to be removed is being constructed as something like os.path.join(dir, '') resulting in just dir. Calling os.remove(dir) results in the errro you indicated. You'll need to avoid this in the delete operation.
def delete(self):
if self.article:
# delete operation
# rest of delete, maybe the default parent delete
super(Foo, self).delete(*args, **kwargs)
There are certainly more complicated things you can do but that should point you in a hopefully helpful direction.
in Django 1.9
i created
from __future__ import unicode_literals
from django.db import models
# Create your models here.
class Foo(models.Model):
article = models.FileField(upload_to='articles', blank=True, default=None, null=True)
then i created two objects
Foo.objects.create(pk=1)
Foo.objects.create(pk=2)
then i tried to delete it
Foo.objects.get(pk=1).delete()
(1, {u'Foo': 1})
its worked like charm!

is there any method available in django models, so that i can access its fieldname using string?

i want to update fields using ajax:
models.py
class EmployerInfo(models.Model):
employer = models.ForeignKey(Employer, unique=True)
address=models.CharField(max_length=100, blank=True)
city=models.CharField(max_length=40, blank=True)
contactinfo.html
<form id="ajax-form">
<fieldset>
<legend>Contact Information</legend>
<label>Address:</label>
<input type="text" id="address" value="{{ empinfo.address }}" />
<label>City:</label>
<input type="text" id="city" value="{{ empinfo.city }}" /> <i class="icon-ok"></i>
</fieldset>
</form>
<script>
$(document).ready(function()
{
$("form#ajax-form").find(":input").change(function()
{
var field_name=$(this).attr("id");
var field_val=$(this).val();
var params ={ param1:field_name, param2:field_val };
$.ajax({ url: "/employer/contactinfo/save/",
dataType: "json",
data: params,
success: setResult
});
});
});
urls.py
.....other urls
url(r'^employer/contactinfo/save/$', 'save_employer_info_ajax'),
view.py
def save_employer_info_ajax(request):
emp=Employer.objects.get(user=request.user)
emp_info=EmployerInfo.objects.get(employer=emp)
f_name=request.GET['param1']
f_value=request.GET['param2']
return HttpResponse(json.dumps({"issuccess": 'yes'}), content_type="application/json")
f_name the name of the field i want to update. lets assume it be 'address'.
how can i access that attribute, (ie emp_info.address) so that i can update (ie emp_info.address=f_value) using emp_info.save() function.
is there any method available other than emp_info.address, so that i can access the field name using string (ie emp_info[f_name]=f_value ) or something??
You could just use getattr baked into python
attr_name = 'employer_id'
if getattr(employee, attr_name) == 3:
...
You can use the update method on the queryset object. It's a bit hacky since you are really only wanting to update a single object though.
def save_employer_info_ajax(request):
emp_info_query = EmployerInfo.objects.filter(employer__user=request.user)
update_dict = {request.GET['param1'] : request.GET['param2'] }
emp_info_query.update(**update_dict)
Notice that I'm using reverse lookup relations to access EmployerInfo based on the user field of the Employer model. Then you construct a dictionary to hold the keys and values you wish to update, and pass that to the update method of the queryset, not the model instance.
You should be using forms for data entry though. There should be validation on all fields that you're entering into your database. You can use dynamic forms to specify the fields you want included based on the values that you submit via ajax.

Displaying images form datastore

I am just testing out displaying images on GAE, I have this little .py
import cgi
import wsgiref.handlers
from google.appengine.api import users
from google.appengine.api import images
from google.appengine.ext import db
from google.appengine.ext import webapp
class ImgUpload(db.Model):
project_name = db.StringProperty()
project_description = db.StringProperty(multiline=True)
img_name = db.StringProperty()
img_img = db.BlobProperty()
date = db.DateTimeProperty(auto_now_add=True)
class UploadPage(webapp.RequestHandler):
def get(self):
self.response.out.write("""<html><body>
<form action="/upload" enctype="multipart/form-data" method="post">
<div><label>Project Name</label></div>
<div><textarea name="title" rows="2" columns "60"></textarea></div>
<div><label>Despcription:</label></div>
<div><textarea name="content" rows="3" cols="60"></textarea></div>
<div><label>Image Name</label></div>
<div><textarea name="imgname" row="1" cols="60"></textarea></div>
<div><label>Image</label></div>
<div><input type="file" name="img"/></div>
<div><input type="submit" value="Upload" /></div>
</form>
</body>
</html>""")
def post(self):
images = ImgUpload()
project_name = self.request.get('title')
project_description = self.request.get('content')
img_img = self.request.get('img')
img_name = self.request.get('imgname')
images.img_img = db.Blob(img_img)
images.project_name = project_name
images.project_description = project_description
images.img_name = img_name
images.put()
self.redirect('/upload')
class DisplayPage(webapp.RequestHandler):
def get(self):
display = db.GqlQuery("SELECT * "
"FROM display "
"WHERE ANCESTOR IS :1 "
"ORDER BY date DESC LIMIT 5"
)
for record in display:
self.response.out.write('<b>%s</> is the Name' % record.project_name)
def main():
application = webapp.WSGIApplication(
[('/', UploadPage),
('/display', DisplayPage)
],
debug=True)
wsgiref.handlers.CGIHandler().run(application)
if __name__=="__main__":
main()
When I run this, I get a KindError: No implementation for kind 'display'. Is this an issue pertaining to lack of a DB key, I know they data is in the store, I can see it in the admin, and I can't see any naming issue (at least off the top of my head, there could be thousands for all that I know). What exactly is causing this error to pop? Thank you in advance for your advice.
In your GQL you are selecting from the "display" kind. You don't have a display kind defined, you have a "ImgUpload" kind defined. Using the object interface to query will help you avoid this type of problem.
I also don't see a parameter being passed to the query for it to filter on, so you're going to get another error there.

Categories