In my Django 1.10 project, I have a model:
class Contact(models.Model):
notes = models.TextField()
...and ModelForm:
class ContactForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ContactForm, self).__init__(*args, **kwargs)
for field_name, field in self.fields.items():
field.widget.attrs['class'] = 'form-control input-sm plain'
if field.required == True:
field.widget.attrs['required'] = ''
class Meta:
model = Contact
fields = ('notes',)
I have two questions regarding this:
Can I make Django render the notes field as div with contenteditable=true rather than textarea?
If yes, how do I automate the form.save() method?
The second question is a bit vague, so I would be grateful for a hint regarding the first question. I have read through the doc, but couldn't find relevant section :(
Question 1: Render a field with a specific HTML tag
In this case, <div contenteditable="true">...</div>
You can customize the widget used to render a form field. Basically, when you declare the field, you have to pass the argument widget=TheWidgetClass. Django has a lot of builtin widgets you can use. But you can also define your own. To know how, you will find many resources on the Internet. Simply search for "django create custom widget" on a search engine, or on SO. Possible answer: Django: How to build a custom form widget?
Since Django doesn't provide any official documentation on how to create custom widget, the smartest way would be to create a class inheriting Django's TextArea and using a custom html template based on original one.
Question 2: Automate form.save() method with such a custom widget
Basically, you have to make sure the value of the div tag is sent with other inputs values to the target view of your form, in POST data. In other words, you have to make sure the content of the div acts like the content of any form field.
Maybe this SO question can help you: one solution could be using JavaScript to copy the content of your div to a hidden textarea tag in the form when the user click on submit button.
Good luck ;)
First one can be done by using jquery. Since django will load the textarea with id='id_notes'. So after that you can select the element and do whatever you want to.
And in second one you can redefine the save method by writing you own save function in forms.py Here is an example for a url shortener.
Save method basically, defines what you want to execute when something is being committed to database.
Related
I have a model like with a file defined like
models.ImageField(upload_to='folder_icons', null=True)
I want to be able to limit the choice of this icon to a few pre created choices.
I there as way I can show the user (staff member) the choices in the django admin perhaps in a dropdown ?
This is similar to where I want a field where you choose between a few different avatars. Is there a custom field somewhere that can do this ?
Thanks
Just as a starting point, you would need to override the ModelAdmin.get_form() method, which will allow you to change the type of input field that Django uses by default for your image field. Here's what it should look like:
from django.forms import Select
class YourModelAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
# 1. Get the form from the parent class:
form = super(YourModelAdmin, self).get_form(request, obj, **kwargs)
# 2. Change the widget:
form.base_fields['your_image_field'].widget = Select(choices=(
('', 'No Image'),
('path/to/image1.jpg', 'Image 1'),
('path/to/image2.jpg, 'Image 2'),
))
# 3. Return the form!
return form
You'll still have some other considerations - for instance, the path/location of the images themselves (placing them in the settings.MEDIA_ROOT would probably be easiest, or at least the first step in trying to make this work). I can also imagine that you might want a more sophisticated presentation of this field, so that it shows a thumbnail of the actual images (see #Cheche's answer where he suggests select2 - that gets a bit more complicated though, as you'll need to craft a custom widget).
All of that said, in terms of just altering the form field that the admin uses so that it offers a dropdown/select field, rather than a file upload field, this is how you would achieve that.
What you need is a widget to render your choices. You could try with select2 or any django adapt to this, like django-select2. Check this.
I am using materializecss to give my django site some material elements. I have put together a form (the 'old' way using html) but now realised I need to use a django form instead. The problem is, these forms don't play well with materialises built in column system (they use classes to determine rows and column spacing). Here is an example of the layout I set up so far. However when defining the form through form.py, it spits out one input per layer.
My question is: what can I do to either a) get django to work with the html-defined form or b) make a 'form template' to give the input fields the appropriate classes?
If you want to see the code I can post some but I'm quite a new coder so it's messy.
Thanks!
There are three ways I can think of off the top of my head.
If you want full control over the HTML form, in a Django template or HTML form, simply map the names of your fields to match the underlying field names in the Django form. This way, when POSTed back to your view, Django will automatically link up the POSTed fields with the Django form fields.
For example, if you have a field username in your Django form (or Django model if using ModelForm), you could have an element <input type="text" name="username" maxlength="40"> (that you can style any way you need) on your HTML form that Django will happily parse into your Django form field, assuming your view is plumbed correctly. There is an example of this method in the Django documentation.
Another way is to customize the Django form field widgets in your Django form definition. The Django documentation talks a little bit about how to do this. This is great for one offs, but is probably not the best approach if you expect to reuse widgets.
The final approach would be to subclass Django form field widgets to automatically provide whatever attributes you need. For example, we use Bootstrap and have subclassed nearly all of the widgets we use to take advantage of Bootstrap classes.
class BootstrapTextInput(forms.TextInput):
def __init__(self, attrs=None):
final_attrs = {'class': 'form-control'}
if attrs is not None:
final_attrs.update(attrs)
super().__init__(attrs=final_attrs)
Then it's simply a matter of letting the Django form know which widget to use for your form field.
class UsernameForm(forms.ModelForm):
class Meta:
model = auth.get_user_model()
fields = ['username']
widgets = {'username': BootstrapTextInput()}
Hope this helps. Cheers!
I am trying to use Parsley.js for validation in my Django project. I am currently at the step where I am trying to add the necessary Parsley attributes to the form fields. Hopefully this is correct, but I was told I need pass the attributes to the widget in this existing form field.
email = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict,
maxlength=75)),label=_("Email"))
My question is how do I a correctly pass the attributes to the form widget so that I can validate this form field with parsley.js? I took a stab at it below, but I know it's incorrect.
email = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict,
maxlength=75, data-required="true", data-trigger="keyup")), label=_("Email"))
Overriding the __init__ function in your form class may be the best option...
class YourClass(ModelForm):
def __init__(self, *args, **kwargs):
self.fields['yourfield'].widget.attrs.update({'yourattribute': 'yourattributevalue'})
You should be able to pass in the attributes directly to field declaration like so:
email = forms.EmailField(widget=forms.TextInput(attrs={'data-trigger':'keyup', 'data-required' : 'true'}), maxlength=75, label=_("Email"))
See django form widget attrs documentation for more details: https://docs.djangoproject.com/en/dev/ref/forms/widgets/#django.forms.Widget.attrs
I have a model and a form like this:
class MyModel(models.Model):
param = models.CharField()
param1 = models.CharField()
param2 = models.CharField()
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ('param', 'param1', 'param2')
Then I have one drop down menu with different values and based on what value is selected I'm hiding and showing fields of MyForm. Now I have to take one step further and render param2 as a CheckboxInput widget if user selects a certain value from a drop down but in other cases it should be standard text field. So how would I do that?
I know this post is almost a year old, but it took me multiple hours to even find a post related to this topic (this is the only one I found, which came up as related when submitting my own question), so I felt the need to share my solution.
I wanted to have a form that would show and require a text field if an option from a dropdown menu matched a value stored in another model. I had a foreignKey relation between two models and I passed an instance of Model1 into the ModelForm for Model2. If a value chosen for a variable in Model2 matched a variable already set in Model1, I wanted to show and require a textfield. It was basically a "choose Other and then enter your own description" scenario.
I did not want the page to reload (I was trying to have this work in both mobile and desktop browsers with the least delay/reloads and using the same code for both), so I could not use the mentioned multiple forms loading in a view option. I started trying to do it with AJAX as suggested above when I realized I was over thinking the problem.
The answer was using JS and clean methods in the form. I added a non-required field (field1) that was not in Model2 to my Model2Form. I then hid this using jQuery and only displayed it (using jQuery) if the value of another field (field2) matched the value of the variable from Model1. To make that work, I did decide to have a hidden < span > in my template with the pk of the variable so I could easily grab it with jQuery. This jQuery worked perfectly for hiding and showing the field correctly so the user could choose the "other" value and then decided to choose a different one instead (and go back and forth endlessly).
I then used a clean method in my Model2Form for field1 that raised a ValidationError if no value was entered when the value in field2 matched my Model1 variable. I accessed that variable by using "self.other = Model1.variable" in my __ init __ method and then referencing that in the clean_field1 method.
I would have liked to have been able to accomplish this without having to hide and show a field with JS, but I think the only solutions for doing so with views or ajax caused delays/reloads that I did not want. Also, I liked the general simplicity of the method I used, rather than having to figure out how to pass partial forms back and forth through the HTTPRequest.
Update:
In my situation, I was creating entries for lost and found items and if the location where the item was found was not a provided option, then I wanted to show a textbox for the user to enter the location. I created a location object that was set as the "other" location and then displayed the textbox when that object was selected as the "found" location.
In forms.py, I added an extra CharField and use a clean method to check if the field is required and then throw a ValidationError if it wasn't filled in:
class Model2Form(forms.ModelForm):
def __init__(self, Model1, *args, **kwargs):
self.other = Model1.otherLocation
super(Model2Form, self).__init__(*args, **kwargs)
...
otherLocation = forms.CharField(
label="Location Description",
max_length=255,
required=False
)
def clean_otherLocation(self):
if self.cleaned_data['locationFound'] == self.other and not self.cleaned_data['otherLocation']:
raise ValidationError("Must describe the location.")
return self.cleaned_data['otherLocation']
Then in my JavaScript, I checked if the value of the "found" location was the "other" location (the value of which I had in a hidden span on my html page). I then used .show() and .hide() on the textbox's parent element as necessary:
$("#id_locationFound").change( function(){
if ($("#id_locationFound").val() == $("#otherLocation").attr("value")){ //if matches "other" location, display textbox; otherwise, hide textbox
$("#id_otherLocation").parent().show();
}else
$("#id_otherLocation").parent().hide();
});
Your best guess would be to trigger a "POST" request when you select something from your drop down menu.
The Value of that "POST" has to correspond your values you use to determine which field you would like to output.
Now you will actually need two forms:
class MyBaseForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ('param', 'param1', 'param2')
class MyDropDownForm(MyBaseForm):
class Meta:
widgets = {
'param2': Select(attrs={...}),
}
So as you can see the DropDownForm has been derived from MyBaseForm to make sure it will have all the same properties. But we have modified the widget of one of the fields.
Now you can update your view. Please note, this is untested Python + Pseudocode
views.py
def myFormView(request):
if request.method == 'POST': # If the form has been submitted...
form = MyBaseForm(request.POST)
#submit button has not been pressed, so the dropdown has triggered the submission.
#Hence we won't safe the form, but reload it
if 'my_real_submitbotton' not in form.data:
if 'param1' == "Dropdown":
form = MyDropDownForm(request.POST)
else:
#do your normal form saving procedure
else:
form = ContactForm() # An unbound form
return render(request, 'yourTemplate.html', {
'form': form,
})
This mechanism does the following:
When the form is submitted it checks if you have pressed the "submit" button or have used a dropdown onChange to trigger a submission. My solution doesn't contain the javascript code you need to trigger the submission with an onChange. I just like to provide a way to solve it.
To use the 'my_real_submitbutton' in form.data construct you will be required to name your submit button:
<input type="submit" name="my_real_submitbutton" value="Submit" />
Of course you can choose any string as Name. :-)
In case of a submit by your dropdown field you must check which value has been selected in this drop down menu. If this value satisfies the condition you want to return a Dropdown Menu you create an instance of DropDownForm(request.POST) otherwise you can leave everything as it is and rerender your template.
On the downside this will refresh your page.
On the upside it will keep all the already entered field values. So no harm done here.
If you would like to avoid the page refresh you can keep my proposed idea but you need to render the new form via AJAX.
I have been working on forms only recently and I am still puzzeld by them.
What I want are standard Forms:
Next Button
Submit Data to Db
Timestamp
Clickable Images with Regions defined where when I click I get to the next page
And
I would like to combine these.
E.g. have a next button + Record the Timestamp.
or
E.g. Click into an Image + Next + Timestamp
If anybody could give me some examples for code that can achieve that or a good online resource on where to get info on that, that would be awesome.
Thanks for the time!!
I'm a little unclear about what you're trying to accomplish, but if you're trying to move data from an HTML form to the database, I'd suggest looking at how to use ModelForms. In a nutshell, you create a model class, like this:
class MyModel(models.Model):
field1 = models.CharField(max_length=50)
Then you create a ModelForm class that references that model:
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
You can render an instance of MyModelForm in a view function. Inside of a POST request in that view, you bind the POST data to the form, validate it, and call save() on it to commit it to the database:
if request.method == 'POST':
form = MyModelForm(request.POST)
if form.is_valid():
model_instance = form.save()
This really isn't a question, I'm not exactly sure what you're trying to accomplish.
If you want to use Django forms, start here, or here.
I assume the stuff you mention about a timestamp should probably be an auto_now field in a model. Take a look at this.
The stuff you mention about buttons and click-able images is really just HTML and has nothing to do with Django. I would try Google for that.