I have a problem with django and forms.
In forms.py I have sth like this:
class MyForm(forms.Form):
def __init__(self, *args, **kwargs):
self.answers = kwargs.pop('answers')
super(AnswersForm, self).__init__(*args, **kwargs)
choices = ["aaaa", "ssssssssss", "ddddddddd"]
self.fields['answers'] = forms.ChoiceField(choices = choices, widget=forms.RadioSelect())
And when i use it in template, I have sth like this:
Answers:
- a
- s
- d
Do you know why I haven't got all words, but only one letter of the word?
choices should be a list (or other iterable) of two-tuples. The first entry in each tuple is the value that gets stored in the database. The second is the human-readable name.
See the documentation on choices for more information.
Thus, your choices list should look more like this:
choices = [("a","aaaa"), ("s", "ssssssssss"), ("d", "ddddddddd")]
As posted, your code is considering each string (aaaa, ssssssss, etc.) to be tuple-like and grabbing the first letter of each for the database representation, and the second letter of each for the human-readable name (used for the label).
Related
Imagine the following two Django models:
class Item(models.Model):
'''
A single Item of something.
'''
name = models.CharField(unique = True)
sets = model.ManyToManyField('Set', blank = True)
def get_supersets(self):
'''
Returns the list of all the supersets the item belongs to EXCLUDING the
directly linked sets.
'''
res = []
for set in self.sets:
res = res + set.get_all_supersets()
return res
class Set(models.Model):
'''
A set of items wich can either contain items or not (empty set allowed).
Sets can be grouped in supersets. Supersets will contain all items of
the related subsets.
'''
name = models.CharField(unique = True)
superset = models.ForeignKey('self', on_delete = models.SET_NULL, null = True, blank = True)
# Note: Self-reference to the same object is avoided by excluding it
from the forms queryset for the superset field.
def get_all_spersets(self):
'''
Returns all supersets of the set.
'''
if self.superset:
return [self.superset] + self.superset.get_all_supersets()
else:
return []
I found two options for implementing the connection between supersets and the corresponding items of the sets in a given superset:
On saving a set or an item update the item_set of the supersets. With this, all relations will be stored in the database. This also needs to include some logic regarding circular relations.
Decide for "direct-links-only", which means an item will only be linked to its directly related set in the database. The relations to the supersets will be found on the fly if requested (e.g get all supersets) with model methods.
For me, option 2 seems much more attractive in terms of data integrity since connected relations will be updated on the fly. However, once a user enters an item -> set relation one needs to make sure the user does not unnecessarily select a superset of a set the item already belongs to, which would break the logic and in the worst case lead to infinite recursion in the model methods to retrieve the supersets.
Since the selection will take place in a form, the Item form looks like this:
class ItemForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.fields['sets'].queryset = Set.objects.all()
# For now, this shows all available sets. One could limit this
queryset to only the sets not in the items get_superset().
However, this would not allow the user to see wich supersets
the item already belongs to.
class Meta:
model = Item
widgets = {'sets': forms.CheckboxSelectMultiple()}
# Available sets are shown as checkboxes.
The Set form looks like this:
class SetForm(forms.ModelForm):
def __init__(self):
self.fields['superset'].queryset = Set.objects.all.exclude(id__exact=self.instance.id)
# As mentioned, avoiding self-reference.
class Meta:
model = Set
My question:
1) How can I show the Items supersets in the ItemForm but avoid that a user chooses them?
2) If a user chooses a set, wich is part of a superset, this superset should immediately become unavailable in the ItemForm. Is something like this possible and how?
I changed manytomany field to token field with the help of bootstrap tag input javascript plugin. Then I am getting comma (,) separates primary keys as input. After splitting from delimiter comma (,) I got the instance of the exact object. Now how to add those objects to the manytomany field. I tried returning list but it didn't work.
Here is my form class
class EventRegistrationForm(forms.ModelForm):
participants = forms.CharField(widget=forms.TextInput(attrs={'autocomplete':'off', 'data-role':'tagsinput'}),required=True)
class Meta:
model=EventRegistration
fields=['event', 'participants', 'teamName', 'feePaid']
def clean_feePaid(self):
feepaid=self.cleaned_data.get('feePaid')
if not feepaid:
raise ValidationError('Please pay the fee First :)')
return feepaid
def clean_participants(self):
participants_data = self.cleaned_data.get('participants')
event = self.cleaned_data.get('event')
participants =[]
for pd in participants_data.split(','):
p = Candidate.objects.get(pk=pd)
participants.append(p)
if not (event.minParticipant <= len(participants) <= event.maxParticipant):
raise ValidationError('Number of Participants exceeded :D')
return participants
def __init__(self, *args, **kwargs):
super(EventRegistrationForm, self).__init__(*args, **kwargs)
self.fields['event'].empty_label = ''
# following line needed to refresh widget copy of choice list
self.fields['event'].widget.choices =self.fields['event'].choices
one can pass list for m2m field.
I just missed form.save_m2m().
I am attempting to push data from a DJANGO view into the Tables object, passing it through as an argument. In this case, I would like to pass a variable called doc_id into a Tables2 object called tableName
In this example, I have set doc_id as 1, and pass it into the
View
def editorView(request):
doc_id = 1
table = tableName(UserProfile.objects.filter(), doc_id=doc_id)
Table
class tableName(tables.Table):
tbl_doc_id = None ## Creating a temporary variable
def __init__(self, *args, **kwargs):
temp = kwargs.pop("doc_id") ## Grab doc_ID from kwargs
super(tableName, self).__init__(*args, **kwargs)
self.tbl_doc_id = temp ## Assign to self.tbl_doc_id for use later
### Do something with tbl_doc_id
modelFilter = model.objects.filter(pk = tbl_doc_id)
When running the debugger, I can see that tbl_doc_id is still assigned as None, rather than 1.
What is the correct way to pass arguments into a Tables2 instance? Is it possible?
EDIT: Adding more information for context.
In the real world scenario, I have a view. That view takes an argument from the URL called doc_id. That doc_id is used to grab an object from a model called 'MaterialCollection', and return it as 'mc'.
'mc' is then passed into the table
View
def editorView(request, doc_id):
try:
mc = MaterialCollection.objects.get(pk = doc_id)
except Material.DoesNotExist:
raise Http404("Document does not exist")
config = RequestConfig(request)
unnassigned_User_Table = unassignedUserTable(UserProfile.objects.filter(), mc=mc)
... Other code + Render ...
From my table, I create a custom LinkColumn. That linkColumn is used to construct a URL based upon a number of Attributes from the model 'UserProfile', and from mc.
Table
class unassignedUserTable(tables.Table):
mc = None
def __init__(self, *args, **kwargs):
temp_mc = kwargs.pop("mc")
super(unassignedUserTable, self).__init__(*args, **kwargs)
self.mc = temp_mc
current_Assignment = "NONE"
new_Assignment = "AS"
assign_Reviewer = tables.LinkColumn('change_Review_AssignmentURL' , args=[ A('user'), current_Assignment, new_Assignment, mc, A('id')], empty_values=(), attrs={'class': 'btn btn-success'})
class Meta:
model = UserProfile
... Setup excludes/sequence/attributes...
In this particular instance. mc has a FK to UserProfile (in a 1:M) relationship.
I see that the name of your table class is tableName so if you want __init__ to work as expected please change the line:
super(unassignedUsers, self).__init__(*args, **kwargs)
to
super(tableName, self).__init__(*args, **kwargs)
Beyond this obvious problem, there are some more issues with your code:
Your classes must start with a capital letter (TableName instead of tableName)
Your table classes should end end with -Table (for example NameTable)
I am using django-tables2 for many years and never needed to pass something in __init__ as you are doing here. Are you sure that you really need to do this?
If you want to filter the table's data the filtering must be done to your view - the table will get the filtered data to display.
In order to make a simple captacha-like field, I tried the following:
class CaptchaField(IntegerField):
def __init__(self, *args, **kwargs):
super(CaptchaField, self).__init__(*args, **kwargs)
self.reset()
def reset(self):
self.int_1 = random.randint(1, 10)
self.int_2 = random.randint(1, 10)
self.label = '{0} + {1}'.format(self.int_1, self.int_2)
def clean(self, value):
value = super(CaptchaField, self).clean(value)
if value != self.int_1 + self.int_2:
self.reset()
raise ValidationError(_("Enter the result"), code='captcha_fail')
return True
Every time my answer is wrong, the label is changed as expected but the test is performed against the first values of int_1 and int_2 and not against the newly randomly generated values.
I don't understand how Field object are created and why I can't access the values of my field.
Thanks in advance
Have a think about how this works in your view. When you render the form, the field is instantiated and sets the label to your random values, which is fine. Now, the user posts back to the view: what happens? Well, the form is instantiated again, as is the field, and the field is set to two new random values. Not surprisingly, this won't match up to the previous value, because you haven't stored that anywhere.
To do anything like this, you need to store state somewhere so it is preserved between requests. You could try putting it in the session, perhaps: or, a better way might be to hash the two values together and put them in a hidden field, then on submit hash the submitted value and compare it against the one in the hidden field. This would probably need to managed at the form level, not the field.
How I would dynamically create a few form fields with different questions, but the same answers?
from wtforms import Form, RadioField
from wtforms.validators import Required
class VariableForm(Form):
def __init__(formdata=None, obj=None, prefix='', **kwargs):
super(VariableForm, self).__init__(formdata, obj, prefix, **kwargs)
questions = kwargs['questions']
// How to to dynamically create three questions formatted as below?
question = RadioField(
# question ?,
[Required()],
choices = [('yes', 'Yes'), ('no', 'No')],
)
questions = ("Do you like peas?", "Do you like tea?", "Are you nice?")
form = VariableForm(questions = questions)
It was in the docs all along.
def my_view():
class F(MyBaseForm):
pass
F.username = TextField('username')
for name in iterate_some_model_dynamically():
setattr(F, name, TextField(name.title()))
form = F(request.POST, ...)
# do view stuff
What I didn't realize is that the class attributes must be set before any instantiation occurs. The clarity comes from this bitbucket comment:
This is not a bug, it is by design. There are a lot of problems with
adding fields to instantiated forms - For example, data comes in
through the Form constructor.
If you reread the thread you link, you'll notice you need to derive the class, add fields to that, and then instantiate the new class. Typically you'll do this inside your view handler.
You're almost there:
CHOICES = [('yes', 'Yes'), ('no', 'No')]
class VariableForm(Form):
def __new__(cls, questions, **kwargs):
for index, question in enumerate(questions):
field_name = "question_{}".format(index)
field = RadioField(question,
validators=[Required()],
choices=CHOICES)
setattr(cls, field_name, field)
return super(VariableForm, cls).__new__(cls, **kwargs)
In my case,
I used a csv and imported it using pandas.
So, this solution allows you to even use different answers if required.
data=pd.read_csv("./temp.csv")
class UserForm(Form):
for i in data:
if data[i][0] == 'textbox':
formElement='TextField("%s",validators=[validators.required()], default="please add content")' %(i)
elif data[i][0] == 'radio':
choice = list(data[i][1:].dropna().unique().tolist())
choiceStr=''
for k in choice:
choiceStr +="('"+k+"','"+k+"'),"
formElement = 'RadioField("%s",validators=[validators.required()],choices=[%s], default="%s")' %(i,choiceStr, choice[0])
elif data[i][0] == 'dropdown':
choice = list(data[i][1:].dropna().unique().tolist())
# choice.remove('X')
choiceStr=''
for k in choice:
choiceStr +="('"+k+"','"+k+"'),"
formElement = 'SelectField("%s",validators=[validators.required()],choices=[%s])' %(i,choiceStr)
exec("%s=%s" % (i,formElement))