In my django formset, I am trying to display a foreign key field with an input instead of a select:
class MinAttend(models.Model):
act = models.ForeignKey(Act)
country = models.ForeignKey(Country)
verbatim = models.ForeignKey(Verbatim)
def __unicode__(self):
return u"%s" % self.verbatim
class MinAttendForm(forms.ModelForm):
country=forms.ModelChoiceField(queryset=Country.objects.all(), empty_label="Select a country")
status=forms.ModelChoiceField(queryset=Status.objects.values_list('status', flat = True).distinct(), empty_label="Select a status")
verbatim=forms.CharField(max_length=300)
class Meta:
model=MinAttend
#fields used for the validation and order
fields = ('country', 'status', 'verbatim')
For the verbatim field, I do have an input box instead of a select but when I want to update a formset, I have the verbatim id instead of its corresponding text:
Here is how I initialize the form:
class MinAttendUpdate(UpdateView):
object=None
model = MinAttend
form_class=MinAttendForm
nb_extra_forms=3
def post(self, request, *args, **kwargs):
attendances=MinAttend.objects.filter(...)
#set the number of forms to the number of ministers + 3 extra form to fill if needed
MinAttendFormSet = modelformset_factory(self.model, form=self.form_class, extra=len(attendances), max_num=len(attendances)+self.nb_extra_forms, can_delete=True)
formset=MinAttendFormSet(queryset=attendances)
I have tried two things:
Instead of the last line I have the following code:
initials=[]
#display text of verbatim instead of id
for index in range(len(attendances)):
initials.append({"verbatim": attendances[index].verbatim.verbatim})
print "initials", initials
formset=MinAttendFormSet(queryset=attendances, initial=initials)
I have overridden the init method of the form:
#~ #verbatim text instead of id for the verbatim field
def __init__(self, *args, **kwargs):
super(MinAttendForm, self).__init__(*args, **kwargs)
instance = kwargs.get("instance", None)
if instance!=None:
print "instance", instance.verbatim.verbatim
self.fields["verbatim"].initial = instance.verbatim.verbatim
None of these methods works, I still get numbers instead of text! What is curious is that I do see text for the verbatim field but only for the three extra forms. Normal?
EDIT - from Bernd Jerzyna comment
In my form:
from django.forms.models import BaseModelFormSet
class MinAttendFormSet(BaseModelFormSet):
def __init__(self, *args, **kwargs):
super(MinAttendFormSet, self).__init__(*args, **kwargs)
for form in self.forms:
#skip extra forms
if not form.empty_permitted:
form.fields['verbatim'].initial= form.instance.verbatim
print "verbatim MinAttendFormSet", form.instance.verbatim
In my view:
from forms import MinAttendForm, MinAttendFormSet
my_formset = modelformset_factory(self.model, form=self.form_class, formset=MinAttendFormSet)
formset = my_formset(request.POST, queryset=attendances)
When I do a print of the text of each verbatim, I see the correct text displayed. However, I still see numbers (primary key ids) in the form of my web page ;(.
What's wrong?
I think something like this should work:
Implement the init method of your formset like
def __init__(self, *args, **kwargs):
...
super(MinAttendFormSet, self).__init__(*args, **kwargs)
for form in self.forms:
form.fields['verbatim'].initial= form.instance.verbatim
That would also require your Verbatim model to have something like:
def __unicode__(self):
return u'%s' % (self.name)
This is my understanding of it...
As a select widget, the verbatim is displayed with the __unicode__ results, but the value 'behind the scenes' is the PK/verbatim-id, ie the HTML <option> tag has a value and 'label'. When you change it to a text input widget, it is expecting you to enter the PK, hence why you are seeing the numbers.
For the solution, I am not sure. You could write some code to accept the verbatim-text, rather than the pk/verbatim-id. The problem with this though, is that if the text is not written exactly, django won't find a match. Also, if more than 1 verbatim has the same text, django wouldn't know which one to use (unless you have set unique=true on the model field for text). You could also set the verbatim-text as the PK.
Perhaps using something like Django-Select2 will give you the desired UI?
Related
I'm working on a form in my Django project. I wanted to add a bootstrap class to my input fields.
I tried to do this with the following code:
class CategoryForm(ModelForm):
class Meta:
model = Category
fields = '__all__'
labels = {
"sub_category":"Sub category (if any):"
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['category_name','sub_category','category_info','category_video'].widget.attrs.update({'class':'form-control'})
But when I load the page I get this:
KeyError at /academy/category_form
('category_name', 'sub_category', 'category_info', 'category_video')
Is this not possible? Do I have to add a new line for every field in my form? So like this:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['category_name'].widget.attrs.update({'class':'form-control'})
self.fields['sub_category'].widget.attrs.update({'class':'form-control'})
self.fields['category_info'].widget.attrs.update({'class':'form-control'})
....
This would make for some long lines of code if I have to do this for every form which does not make sense of follow DRY.
Within the form fields is a dictionary containing the form fields. It is not doing what you expect it to do ...your list of keys is interpreted as a tuple and that tuple is not in the form fields contained, that results in the mentioned KeyError.
Now to your attempt to not repeat yourself ... you can use a loop to avoid this:
for key in ('category_name', 'sub_category', 'category_info', 'category_video'):
self.fields[key].widget.attrs.update({'class':'form-control'})
I am newbie with Django and I get stucked trying to pass the value from a html table rendered with django-tables2 to a form.
view.py
def configView(request):
form = ConfigForm(request.POST or none)
if form.is_valid():
save_it = form.save(commit=False)
save_it.save()
Messages.success(request, 'Configuracion Actualizada')
return HttpResponseRedirect('/monitor/')
return render_to_response("config.html",
locals(),
context_instance=RequestContext(request))
This is my forms.py
class ConfigForm(forms.ModelForm):
class Meta:
model = Config
def __init__(self, *args, **kwargs):
super(ConfigForm, self).__init__(*args,**kwargs)
self.fields['id_proveedor'].initial = kwargs.pop('id_proveedor',None)
But I don't know how to retrieve and pass the value to theform.
I need pass the values from the cells 0, 2 and 6.
Any advice or snippet will be appreciated.
Thanks in advance
I would try this:
class ConfigForm(forms.Form):
def __init__(self, *args, **kwargs):
your_variable_to_pass = kwargs.pop("your_variable_to_pass")
super(ConfigForm, self).__init__(*args,**kwargs)
self.fields['id_proveedor']= forms.FieldClass(attribute=your_variable_to_pass)
id_proveedor = FieldClass()
where, 'FieldClass' is whatever field you choose (i.e. ChoiceField, CharField) and
attribute is the attribute to pass (your variable), i.e. 'choices', 'initial' etc.
thus, it may look like this:
self.fields['id_proveedor']= forms.ChoiceField(choices=your_variable_to_pass)
id_proveedor = ChoiceField()
Notice indentation - you assign value of the attribute to pass in the constructor!; in case of ChoiceField choices is a list of tuples, i.e. (('1', 'First',), ('2', 'Second',)); I use Forms instead of ModelForm as super or base class in this example
Then, in the views: f = ConfigFrom(request.POST, your_variable_to_pass=your_variable_to_pass)
notice your_variable_to_pass=your_variable_to_pass otherwise it'll generate a key error
I hope, it helps!
I am writing an Edit form, where some fields already contain data. Example:
class EditForm(forms.Form):
name = forms.CharField(label='Name',
widget=forms.TextInput(),
initial=Client.objects.get(pk=??????)) #how to get the id?
What I did for another form was the following (which does not work for the case of the previous EditForm):
class AddressForm(forms.Form):
address = forms.CharField(...)
def set_id(self, c_id):
self.c_id = c_id
def clean_address(self):
# i am able to use self.c_id here
views.py
form = AddressForm()
form.set_id(request.user.get_profile().id) # which works in the case of AddressForm
So what is the best way to pass an id or a value to the form, and that could be used in all forms for that session/user?
Second: is it right to use initial to fill in the form field the way I am trying to do it?
You need to override the __init__ method for your form, like so:
def __init__(self, *args, **kwargs):
try:
profile = kwargs.pop('profile')
except KeyError:
super(SelectForm, self).__init__(*args, **kwargs)
return
super(SelectForm, self).__init__(*args, **kwargs)
self.fields['people'].queryset = profile.people().order_by('name')
and, obviously, build your form passing the right parameter when needed :)
I need to pass an instance variable (self.rank) to be used by a class variable (provider) (see the commented out line below).
Commented out, the code below works. But I'm pretty sure I shouldn't be trying to pass an instance variable up to a class variable anyway. So I'm dumbfounded as to how to accomplish my goal, which is to dynamically filter down my data in the ModelChoiceField.
As you can see, I already overrided ModelChoiceField so I could beautify the usernames. And I also subclassed my basic SwapForm because I have several other forms I'm using (not shown here).
Another way of saying what I need ... I want the value of request.user in my Form so I can then determine the rank of that user and then filter out my Users by rank to build a smaller ModelChoiceField (that looks good too). Note that in my views.py, I call the form using:
form = NewSwapForm(request.user)
or
form = NewSwapForm(request.user, request.POST)
In forms.py:
from myapp.swaps.models import Swaps
from django.contrib.auth.models import User
class UserModelChoiceField(forms.ModelChoiceField):
""" Override the ModelChoiceField to display friendlier name """
def label_from_instance(self, obj):
return "%s" % (obj.get_full_name())
class SwapForm(forms.ModelForm):
""" Basic form from Swaps model. See inherited models below. """
class Meta:
model = Swaps
class NewSwapForm(SwapForm):
# Using a custom argument 'user'
def __init__(self, user, *args, **kwargs):
super(NewSwapForm, self).__init__(*args, **kwargs)
self.rank = User.objects.get(id=user.id).firefighter_rank_set.get().rank
provider = UserModelChoiceField(User.objects.all().
order_by('last_name').
filter(firefighter__hirestatus='active')
### .filter(firefighter_rank__rank=self.rank) ###
)
class Meta(SwapForm.Meta):
model = Swaps
fields = ['provider', 'date_swapped', 'swap_shift']
Thanks!
You can't do it that way, because self doesn't exist at that point - and even if you could, that would be executed at define time, so the rank would be static for all instantiations of the form.
Instead, do it in __init__:
provider = UserModelChoiceField(User.objects.none())
def __init__(self, user, *args, **kwargs):
super(NewSwapForm, self).__init__(*args, **kwargs)
rank = User.objects.get(id=user.id).firefighter_rank_set.get().rank # ??
self.fields['provider'].queryset = User.objects.order_by('last_name').filter(
firefighter__hirestatus='active', firefighter_rank__rank=rank)
I've put a question mark next to the rank line, because rank_set.get() isn't valid... not sure what you meant there.
I have a Django admin form.
And now I want to fill it's initial field with data based on my model. So I tried this:
class OrderForm(forms.ModelForm):
class Meta:
model = Order
email = CharField(initial="null", widget=Textarea(attrs={'rows': 30, 'cols': 100}))
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
products = kwargs['instance'].products.all()
self.message = purchase_message % (
"".join(["<li>" + p.name + ": " + str(p.price) + "</li>" for p in products]),
reduce(lambda x, y:x + y.price, products, 0)
)
# and here I have a message in self.message variable
super(OrderForm, self).__init__(*args, **kwargs)
At this point i don't know how to access email field to set it's initial value before widget is rendered. How can i do this?
Assuming the value is based on 'request' you should use this:
class MyModelAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
form = super(MyModelAdmin, self).get_form(request, obj, **kwargs)
form.base_fields['my_field_name'].initial = 'abcd'
return form
Since Django 1.7 there is a function get_changeform_initial_data in ModelAdmin that sets initial form values:
def get_changeform_initial_data(self, request):
return {'name': 'custom_initial_value'}
EDIT: Apart from that, #Paul Kenjora's answer applies anyway, which might be useful if you already override get_form.
In case of inline (InlineModelAdmin) there is no get_changeform_initial_data. You can override get_formset and set formset.form.base_fields['my_field_name'].initial.
I'm not too sure what you need to set email to, but You can set the initial values in lots of different places.
Your function def init() isn't indented correctly which i guess is a typo? Also, why are you specifically giving the email form field a TextInput? It already renders this widget by default
You can set the email's initial value in your form's initialized (def __ init __(self))
(self.fields['email'].widget).initial_value = "something"
or in the model.py
email = models.CharField(default="something")
or as you have in forms.py
email = models.CharField(initial="something")
I needed the first solution of pastylegs since the other ones overwrite the whole Widget including, for example, the help text. However, it didn't work for me as he posted it. Instead, I had to do this:
self.fields['email'].initial = 'something'
In my case, I was trying to do a personalized auto-increment(based on current data and not a simple default) in a field of a django admin form.
This code is worked for me (Django 1.11):
from django import forms
class MyAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.initial['field_name'] = 'initial_value'