I'm using modelformset factory to generate formset from model fields. Here i want to make only the queryset objects as readonly and other (extra forms) as non readonly fields
How can i achieve this?
AuthotFormSet = modelformset_factory(Author, extra=2,)
formset = AuthorFormSet(queryset=Author.objects.all())
In Above formset i wanted to display all the queryset objects as readonly, and remaining extra forms as non readonly fields. How can i achive this?
if i used,
for form in formset.forms:
form.fields['weight'].widget.attrs['readonly'] = True
This will convert all the forms (including extra) fields to readonly which i dont want.
And also i'm using jquery plugin to add form dynamically to the formset
I'd recommend specifying a form to use for the model, and in that form you can set whatever attributes you want to read only.
#forms.py
class AuthorForm(forms.ModelForm):
class Meta:
model = Author
def __init__(self, *args, **kwargs):
super(AuthorForm, self).__init__(*args, **kwargs)
if self.instance.id:
self.fields['weight'].widget.attrs['readonly'] = True
#views.py
AuthorFormSet = modelformset_factory(Author, extra=2, form=AuthorForm)
You can also put in your template :
{{form.management_form}}
{% for i in form %}
<p>{{ i.instance.readonly_field }}</p>
{{i.as_p}}
{% endfor %}
and not put the readonly_field in ModelForm.Meta.fields.
just need to check if the instance has id, like this:
if self.instance.id
before setting it as read-only
I used python long back. Hope this helps . But if you wish to control fields display using jquery
$('.class').attr('readonly', true);
or
$('#id').attr('readonly', true);
Related
I have two separate class-based views and would like to keep their functionality, and have the two CBVs point to the same template (I'm trying to bring two separate forms into a single page).
More specifically, I am trying to subclass/combine this email view, and this password change view, so that I can have them point to the same template, as I ultimately would like both forms on the same page.
I've attempted to do this by subclassing them into my own views:
class MyEmailUpdateView(LoginRequiredMixin, EmailView):
template_name = 'account/account_settings'
success_url = reverse_lazy('settings')
def form_valid(self, form):
return super(SettingsUpdateView, self).form_valid(form)
class MyPasswordUpdateView(LoginRequiredMixin, PasswordChangeView):
template_name = 'account/account_settings'
success_url = reverse_lazy('settings')
def form_valid(self, form):
return super(SettingsUpdateView, self).form_valid(form)
But I am now finding out due to errors, one by one, that it appears nothing from the parent class is actually transferred over to my custom class unless I manually bring it in(success_url, methods, etc). Even then, the code from the original classes that I am subclassing is pointing elsewhere.
SO, when combining these two views, do I need to copy all of the original code into my custom subclass views?
Is this the proper way to accomplish it? How can I combine these two views? I'm ultimately looking for a way to have both of their forms on a single page in my own app. Is there possibly a simpler way to accomplish this using the library's provided templates?
You can use Django Multi Form View to combine many forms in view
After install you can use it like below:
class MultiFView(MultiFormView):
form_classes = {
'email_form' : AddEmailForm,
'change_password_form' : ChangePasswordForm
}
record_id = None
template_name = 'web/multi.html'
def forms_valid(self, forms):
email = forms['email_form'].save(commit=False)
email.save()
return super(MultiFView, self).forms_valid(forms)
and in template:
{{ forms.email_form.as_p }}
{{ forms.change_password_form.as_p }}
I think You can use the default class in django to a achieve the same result.
As far as i understood i got the scenario like this we have two django forms and we need it to be used in same template if that is the scenario we can use the LoginView from django.contrib.auth.views which has several customizable option like you can give the additional form like this
class LoginUserView(LoginView):
authentication_form = LoginForm
extra_context = {"register_form": RegistrationForm}
template_name = 'accounts/index.html'
it will be using the get context data method to update the form which you will be able to get in the template and use accordingly .If you are not wishing to use the code like this you can still use it like this
class MyEmailUpdateView(LoginRequiredMixin, EmailView):
form_class = EmailForm
template_name = 'account/account_settings'
success_url = reverse_lazy('settings')
def get_context_data(self, **kwargs):
context = super(MyEmailUpdateView, self).get_context_data(**kwargs)
context['password_reset_form'] = ResetPasswordForm
return context
def form_valid(self, form):
return super(MyEmailUpdateView, self).form_valid(form)
Then you can handle your post in the form valid accordingly as your requirement.
Hope that helps get back if you need any additional requirement.
This can be solved by implementing a (kind of) partial update method for your view.
Tools:
First protect your sensitive data:
sensitive_variables decorator:
If a function (either a view or any regular callback) in your code uses local variables susceptible to contain sensitive information, you may prevent the values of those variables from being included in error reports using the sensitive_variables decorator
sensitive_post_parameters() decorator:
If one of your views receives an HttpRequest object with POST parameters susceptible to contain sensitive information, you may prevent the values of those parameters from being included in the error reports using the sensitive_post_parameters decorator
Create some code after:
Use ModelForm to create a form with specific fields of interest for your view. Don't include the password field, because we will add it in a different way to suit our needs:
myapp/forms.py:
class PartialUpdateForm(forms.ModelForm):
new_password = forms.CharField(
required=False, widget=forms.widgets.PasswordInput
)
class Meta:
model = MyUser
fields = ('email', 'other_fields',...)
Here you can use exclude instead of fields if you want to keep every other field except the password: ex. exclude = ('password',)
Use an UpdateView to handle the hustle and override it's form_valid method to handle the partial update:
myapp/views.py:
class MyUpdateView(UpdateView):
template_name = 'account/account_settings'
form_class = PartialUpdateForm
success_url = reverse_lazy('settings')
#sensitive_variables('new_password')
#sensitive_post_parameters('new_password')
def form_valid(self, form):
clean = form.cleaned_data
new_password = clean.get('new_password')
if new_password:
form.instance.password = hash_password(new_password)
return super().form_valid(form)
Finally, create a url for that view:
myapp/urls.py:
...
url(r'^your/pattern/$', MyUpdateView.as_view()),
This way you can handle:
Email AND password update at the same time
Only email update.
Only password update.
Using code from this solution: Django: Only update fields that have been changed in UpdateView
how can i add numeration to every form in formset in django admin panel. I need next number for every new added form
class QuestionAnswerInline(admin.TabularInline):
model = QuestionAnswer
formset = SetTestQuestionAnswerFormSet
fields = ('question_answer_text','right_mark')
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
...
]
inlines = [QuestionAnswerInline]
A couple of ways to do that:
Override edit view template for this specific admin class and add enumeration within template with {{ forloop.counter }}. This is probably easyest way of doing this
Override formset class in your admin to provide counter for all the inline forms. You need to do quite abit of magic to achieve that, but that is also possible. Basically you extend the inline admin class get_formset method to create the forms, provide the counter data and then non-editable field to display that data.
I've been working on creating a model form with django-crispy-forms, with Django 1.8.4 and django-crispy-forms-1.5.2. I am failing to alter the form tag attributes.
I have tried setting self.helper.form_tag = False, but it still produces a <form> tag. I've tried setting other attributes like the form_action, but this does not work either, the form tag remains unchanged (the final HTML is still just <form>).
In the views.py:
class RegisterStudentView(CreateView):
template_name = "register_student.html"
model = Student
form_class = StudentRegistrationForm
def form_valid(self, form):
form.save()
return HttpResponseRedirect('dashboard')
In the forms.py:
class StudentRegistrationForm(ModelForm):
def __init__(self, *args, **kwargs):
super(StudentRegistrationForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
class Meta:
model = Student
exclude = ['is_active', 'is_overdue', 'personid', 'tertiary_cell']
Any help would be greatly appreciated.
As awwester commented, the problem was having a hard coded <form> tag around the crispy forms function:
<form>
{% crispy %}
</form>
Basically, crispy forms used the existing form tag and did not create the new one I wanted.
I have this model:
class Gallery(models.Model):
HeadImage = models.ImageField(upload_to="gallery")
With this form:
class GalleryForm(ModelForm):
class Meta:
model = Gallery
And this view:
gform = GalleryForm(request.POST, request.FILES, instance=galleryInstance)
In template a filled form is shown. For the HeadImage field it shows a link to an image related to the HeadImage field, and a fileinput with a change label:
{{ gform.HeadImage }}
Now instead of a link to the picture, I want to put the image into an img tag. I do this in the template as follows:
<img src={{ gform.HeadImage.value }}/>
What should I do so that the link doesn't show in the form?
To prevent it from showing, use any of these three options:
Set editable=False on the model field;
Use the fields attribute of the ModelForm's inner Meta class.
Use the exclude attribute of the ModelForm's inner Meta class.
Which one to use depends on how often you want the field to show (never, or in select cases). See the Django docs for more information on this.
I have a Django form with a RegexField, which is very similar to a normal text input field.
In my view, under certain conditions I want to hide it from the user, and trying to keep the form as similar as possible. What's the best way to turn this field into a HiddenInput field?
I know I can set attributes on the field with:
form['fieldname'].field.widget.attr['readonly'] = 'readonly'
And I can set the desired initial value with:
form.initial['fieldname'] = 'mydesiredvalue'
However, that won't change the form of the widget.
What's the best / most "django-y" / least "hacky" way to make this field a <input type="hidden"> field?
This may also be useful: {{ form.field.as_hidden }}
If you have a custom template and view you may exclude the field and use {{ modelform.instance.field }} to get the value.
also you may prefer to use in the view:
field = form.fields['field_name']
field.widget = field.hidden_widget()
but I'm not sure it will protect save method on post.
edit: field with multiple values don't supports HiddenInput as input type, so use default hidden input widget for this field instead.
an option that worked for me, define the field in the original form as:
forms.CharField(widget = forms.HiddenInput(), required = False)
then when you override it in the new Class it will keep it's place.
Firstly, if you don't want the user to modify the data, then it seems cleaner to simply exclude the field. Including it as a hidden field just adds more data to send over the wire and invites a malicious user to modify it when you don't want them to. If you do have a good reason to include the field but hide it, you can pass a keyword arg to the modelform's constructor. Something like this perhaps:
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
def __init__(self, *args, **kwargs):
from django.forms.widgets import HiddenInput
hide_condition = kwargs.pop('hide_condition',None)
super(MyModelForm, self).__init__(*args, **kwargs)
if hide_condition:
self.fields['fieldname'].widget = HiddenInput()
# or alternately: del self.fields['fieldname'] to remove it from the form altogether.
Then in your view:
form = MyModelForm(hide_condition=True)
I prefer this approach to modifying the modelform's internals in the view, but it's a matter of taste.
For normal form you can do
class MyModelForm(forms.ModelForm):
slug = forms.CharField(widget=forms.HiddenInput())
If you have model form you can do the following
class MyModelForm(forms.ModelForm):
class Meta:
model = TagStatus
fields = ('slug', 'ext')
widgets = {'slug': forms.HiddenInput()}
You can also override __init__ method
class Myform(forms.Form):
def __init__(self, *args, **kwargs):
super(Myform, self).__init__(*args, **kwargs)
self.fields['slug'].widget = forms.HiddenInput()
If you want the field to always be hidden, use the following:
class MyForm(forms.Form):
hidden_input = forms.CharField(widget=forms.HiddenInput(), initial="value")
If you want the field to be conditionally hidden, you can do the following:
form = MyForm()
if condition:
form.fields["field_name"].widget = forms.HiddenInput()
form.fields["field_name"].initial = "value"
Example of a model:
models.py
from django.db import models
class YourModel(models.Model):
fieldname = models.CharField(max_length=255, default="default")
In your form, you can add widgets with ModelForm. To make it hidden add 'type': 'hidden' as shown below👇
forms.py
from .models import YourModel
from django import forms
class YourForm(forms.ModelForm):
class Meta:
model = YourModel
fields = ('fieldname',)
widgets = {
'fieldname': forms.TextInput(attrs={'type': 'hidden'}),
}
If you don't know how to add it to your views.py file, here is some videos about it.
If you use Function Based View:
https://www.youtube.com/watch?v=6oOHlcHkX2U
If you use Class Based View:
https://www.youtube.com/watch?v=KB_wDXBwhUA
{{ form.field}}
{{ form.field.as_hidden }}
with this jinja format we can have both visible form fields and hidden ones too.
if you want to hide and disable the field to protect the data inside. as others mentioned use the hiddenInput widget and make it disable
in your form init
example:
if not current_user.is_staff:
self.base_fields['pictureValid'].disabled = True
self.base_fields['pictureValid'].widget = forms.HiddenInput()
With render_field is easy
{% render_field form.field hidden=True %}
You can just use css :
#id_fieldname, label[for="id_fieldname"] {
position: absolute;
display: none
}
This will make the field and its label invisible.