0

Allright, I've been struggling with this for a while now and I can't really figure out how I should be doing this in Python / Django. Maybe there is something fundamentally wrong with my database. In that case, help is appreciated.

This is a print screen that contains my database structure: https://gyazo.com/bcb0c1090a005c581f3a62ba24d9302e

Note, a program can have an arbitrary amount of Chars (Characteristics), which each can have an arbitrary amount of cats (Categories).

Now, in this particular test program, i want to add a Risk by means of a form. This Risk needs to answer all the Chars, and choose one of the Cats for each Car. (Test Risk 1: {Occupancy: Farm, Construction: Metal Frame, Sprinklers: Yes, Protection Class: 1})

How do I do this?

This is my Python code for Risks thus far:

def add_new_risk(request, id=None):
program = get_object_or_404(Program, id=id)
new_risk_form = NewRiskForm(request.POST or None)
if new_risk_form.is_valid():
    new_risk = new_risk_form.save(commit=False)
    new_risk.save()
    return HttpResponseRedirect(program.get_absolute_url() + 'risks/create/' + str(new_risk.id))
context = {
"form": new_risk_form
}
return render(request, 'form.html', context)

Thus first, I choose which program I want to add the risk to, then, I need to answer all the characteristics that are contained within that program. I tried this:

def answer_risk_chars(request, id=None, program_id=None):
program = get_object_or_404(Program, id=program_id)
risk = RiskClass(program=Program.objects.get(id=program.id))
chars = program.char_set.all()
for c in chars:
    setattr(risk, c.label, models.CharField(max_length=200, choices=c.cat_set.all(), default=c[0]))

However, I do not know how to construct a form based on 'risk', which should contain all the correct attributes. I do not even know whether this is possible.

Is there any way to do this?

Thanks in advance

T. Pijl
  • 23
  • 5

1 Answers1

0

The answer is "yes". You can construct any Django form or other entity dynamically at runtime rather than as a declarative class. You may find it useful to peruse my self-answered questiion here How to reduce Django class based view boilerplate. It's not about forms, but it is a pretty full exposition of how you get from a Django-style declarative class to a dynamic construction of the same thing.

If you google "Django dynamic form" you will find a lot more form-related stuff. You may find that what you need has already been done (try searching the Django packages web site).

If you need to roll it yourself, you need to read and re-read the (quite brief) documentation of the three-argument form of Python's type builtin, which constructs classes. Then you'll try something like

MyDynamicFormClass  = type( 'MyDynamicForm', (forms.Form, ), dict_of_fields)

where dict_of_fields might be constructed something like (incomplete, outline code):

dict_of_fields = {}

for cat in cats:
    choices = [ ]
    for char in get_characteristics( cat):
        choices.append( (db_value_for_char, user_label_for_char) )

    dict_of_fields[ cat_name ] = forms.ChoiceField( choices, ...)    

If there are a set of fields that are the same for all such forms, you could inherit from MyBaseForm rather than from forms.Form to include them. There are also well-known ways of modifying an instance of MyBaseForm to add extra fields at runtime, rather than creating a completely new class from nothing.

Community
  • 1
  • 1
nigel222
  • 7,582
  • 1
  • 14
  • 22
  • Thanks for the answer. Will start digging into this right now. If any questions occur, will comment on this one. Still struggeling with Python / Django (3 weeks of experience), so will take a while to fully grasp this I guess. – T. Pijl Oct 25 '16 at 07:16
  • All right. I've got my dict of fields working in the Python shell (I get 99% of the things that I want in my Python shell). But my main problem is the implementation in the Django script. I have absolutely no idea what to define a class, how to define my form, etc. etc. that it really is working. – T. Pijl Oct 25 '16 at 07:45
  • Oh, enter already posts the comment and I can't edit it. The next thing is. I want to just give in the program it has to be added to. Lets say I have the URL http://127.0.0.1:8000/programs//risks/create/. Then it needs to automatically take the form which has all the characteristics from program . If that makes sense – T. Pijl Oct 25 '16 at 07:54
  • In brief: url `r'^programs/(?P)\w+)/risks/create`. the associated view gets passed the pattern-matched program id. Use that to pull the corresponding categories and characteristics from the database. Use those to dynamically create the form class. Instantiate the form class and feed it to the user in the usual way. – nigel222 Oct 25 '16 at 08:46
  • Okay, I've got my dynamic form (as far as I know), which is working. However: I've defined my Risk Class as a 'BaseClass' and I want to add 'subclasses' to each individual Program. However, it does not save the dynamic form fields to my database, just the base attributes. Any idea how to fix this? I've looked into: http://stackoverflow.com/questions/32335349/how-do-i-create-and-save-dynamic-fields-in-django-modeladmin but can't really get any idea on what i have to do (declared_fieldsets is no longer working) – T. Pijl Oct 25 '16 at 10:57
  • AFAIK you can't save a dynamic form class. You have to recreate it every time the view is called, from the "categories and characteristics" tables in the the DB at that time. Start with a set of simple variables that you *could* have pulled in with a DB query, don't actually implement that query until the hard-coded test view is working. It's almost certainly the DB access to get categories and characteristics that will dominate the performance, not constructing a new class and instance "on the fly". Not sure if this is what you are asking ... maybe get more fluent with simpler things first. – nigel222 Oct 25 '16 at 12:04
  • Hmm. Maybe this question gives a better understanding of what i want: http://stackoverflow.com/questions/40238993/add-modelform-fields-as-attribute-to-object Furthermore, I only want to store the answers that are given. Some background info: These values need to be stored such that depending on a certain set of characteristics, a premium of that particular risk can be calculated. And about getting more fluent, this is part of my Master's Thesis and I don't really have that much time to learn everything about Python haha – T. Pijl Oct 25 '16 at 12:12
  • One more question. I've got my form working. But the answers given are linked to a Category class. When I'm giving an answer in the formfield, i want to store this answer as an Answer class. This Answer class has a ForeignKey of a Category class. That is, I want to solve this: Answer.objects.create(risk=Risk.objects.get(id=program.id), category=new_risk_form[char.label].value()) However, I get the string value, what I want is the Category class (In my Form I have the following line of code: choices.append( (cat, cat.label) ), where cat = a Category instance. Can you help me out please? – T. Pijl Nov 04 '16 at 11:44