Closed amitjindal closed 3 years ago
I have to tell you, you've done it all wrong.
https://github.com/barseghyanartur/django-fobi#rendering-forms-using-third-party-libraries
It is possible to render the form using third party libs (like crispy forms or other), but in order to get to a form, you need more than just a form object. You have to reproduce most of the parts of the form rendering view.
https://github.com/barseghyanartur/django-fobi/blob/v0.17.x/src/fobi/views.py#L2109
@barseghyanartur Thanks for the advice. I spent some time reading the code and found out how to make it work.
I reduced the code to this:
def get_dynamic_form(request: HttpRequest, form_slug: str, initial):
"""View created form.
:param django.http.HttpRequest request:
:param string form_slug:
:return DynamicForm
"""
form_cls = get_dynamic_form_class(request, form_slug)
form = None
if request.POST:
form = form_cls(request.POST, request.FILES)
else:
# Providing initial form data by feeding entire GET dictionary to the form, if ``GET_PARAM_INITIAL_DATA``
# is present in the GET.
kwargs = {}
if GET_PARAM_INITIAL_DATA in request.GET:
kwargs = {'initial': request.GET}
form = form_cls(**kwargs)
return form
def get_dynamic_form_class(request: HttpRequest, slug: str):
try:
form_entry = FormEntry.manager.get(slug=slug)
except FormEntry.DoesNotExist as err:
raise None
form_element_entries = form_entry.formelemententry_set.all()
# This is where the form is being built dynamically.
form_cls = assemble_form_class(form_entry, form_element_entries=form_element_entries, request=request)
return form_cls
Now in the view I can simply do:
class EmployeeCreateView(CreateView):
model = Employee
form_class = EmployeeForm
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
diary_form = get_dynamic_form(self.request, "diary")
ctx["diary_form"] = diary_form
return ctx
def post(self, request, *args, **kwargs):
"""
Handle POST requests: instantiate a form instance with the passed
POST variables and then check if it's valid.
"""
form = self.get_form()
diary_form = get_dynamic_form(request, "diary")
if form.is_valid() and diary_form.is_valid():
print(diary_form.cleaned_data)
return self.form_valid(form)
else:
return self.form_invalid(form)
<form method="post">
{% csrf_token %}
{{ form | crispy }}
{{ diary_form | crispy }}
<button class="btn btn-primary pull-right" type="submit">{% trans "Save" %}</button>
</form>
Leaving this here in case someone else needs it.
I later realized this is GPL licensed and I won't be able to use this in our project. Thanks for your work on this anyway. This is a really good project.
Updated: Added get_dynamic_form_class
@amitjindal:
As of licensing, it's dual license (GPL/LGPL). Thus, I'm more than sure you can use it in your project. :)
@barseghyanartur Thank you for pointing that out. I again missed it. I sincerely thank you for helping me with this and letting me know about the LGPL. I will do further work with this if possible. If I end up using this (or even not) and I find some interesting use cases I will post them here.
@amitjindal:
In the example you've given, you probably forgot to include the implementation of the get_dynamic_form
function (which should be some wrapper around fobi.dynamic.assemble_form_class
). That would be useful for others to see. And I even might add this as an example to the FAQ.
@barseghyanartur Its there. That's the first part of my sample above.
I also need to find how to separate/use form creation/editing as I need them to show nested in an existing form. I will post that code also here once I find out.
BTW the sample above works correctly. I am bypassing all handlers etc as I simply want the form to validate the data and give me cleaned_data. I plan to store it in a JSONField.
Once I cleanup I will provide the cleaned up code as well. I was thinking if there is a way to build a custom FormField that encapsulated the Dynamic form you generated. Then the form field will directly use the cleaned data to populate itself. But I am not as familiar with custom form fields so I am at a loss here. Will try this week.
@barseghyanartur Its there. That's the first part of my sample above.
Ah, yep, but the get_dynamic_form_class
isn't.
As of custom FormField you've mentioned, I've done similar things in Django (a long long time ago). You can even define a single form field which produces multiple fields.
@barseghyanartur My bad. Added it to sample above. My mind is not concentrated today. Sorry.
I will try the custom form field or some way so that it integrates properly with others. Will let you know.
Hi, I am trying to implement the following but am not sure how to go about it. I tried searching in docs as well as examples and even source code. But unable to understand how I would do the following. I think this should be easy but I am just not able to get this working.
Use Case
I want to have a model that can be extended at runtime with data stored in JSON field.
Tried this
This is just an example. My use case is a bit more complicated. Say I have a Employee. I want the ability for this employee model to have custom fields. I want to go into the admin and add some fields.
I visited /fobi/* pages and already created a form named 'contrator' (slug also contractor).
Now I want to display this underneath an employee form.
So trying something like:
Result
This did not work. I get an error:
'FormEntry' object has no attribute 'is_bound'
I also tried to add
snippets/form_snippet.html
for crispy but that also did not work. I must be missing something small. What object do I pass or how do I render this form?Ideally I would like to capture the submission in a JSON field in Employee.
Please help.