Skip to main content

Bind forms to arbitrary objects with ease!

Project description

Django general-purpose forms

Create forms, bind them to arbitrary objects, add specific behaviors with ease!

This package will handle the generic foreign key part of your forms, and provide a view tied to an url to submit your forms to. It can also be used to save your forms to the database (all content goes in one field), and send an email to one or more adresses related to your arbitrary objects.

You can still use your own views to handle form submission if you want to, for example by creating a Mixin (not included in this package).

Requirements

Tested with Python>=3.12 and Django>=3.2.

Also tested from an apphook (django-cms).

Install

  1. Install the package
pip install django-general-purpose-form
  1. Add it to your INSTALLED_APPS
    "django_general_purpose_forms",
  1. Add the url to your urls.py (if you want to use the same view for all forms submission)
    path("dgpf/", include("django_general_purpose_forms.urls"),),
  1. Configure your forms (see below)
  2. That's all folks!

Config

See example below for a full example.

DGPF is designed to be as simple as possible to implement, but also to be as flexible as possible, so you can use it in many different ways. It will handle a few things automatically for you:

  • you must inherit from django_general_purpose_forms.forms.AbstractGeneralPurposeForm, which will handle the genericforeignkey part of the form for you (retrieve the object)
  • when the form is submitted, the generic view in django_general_purpose_forms.views.HandleFormView will retrieve the form class from the DJANGO_GENERAL_PURPOSE_FORMS_CONFIG setting, and populate the form from the request data.
    • It will then redirect to the success url defined in the form config.

Configure your forms in settings.py

You need to define an identifier, a form path and a success url name for each form you want to use, this will be used to retrieve the form class from its identifier in DGPF HandleFormView, and to redirect to the success url after the form is submitted.

The key/identifier needs to be url-friendly, as it will be passed in the url when the form is submitted.

DJANGO_GENERAL_PURPOSE_FORMS_CONFIG = {
    "key": {
        "form_path": "path.to.FormClass",
        "success_url": "name_of_the_url:to_redirect_to",
    },
}

The view will try to call your success url with the pk of the object as a keyword argument. If it fails, it will call it without any arguments.

Update your models.py

In order to use django-general-purpuse-forms, you'll need to add a get_dgpf_form method to your model, which will return an instance of the form class you defined in your settings.

# add this vvvv
from .forms import MyForm
# add this ^^^

# [...]

class MyModel(models.Model):
    # [...]

    # add this vvvv
    def get_dgpf_form(self):
        return MyForm(instance=self)  # <-- instance here is really important
    # add this ^^^

Create your form in forms.py

You can define as much fields as you want here.

The save method will be called when the form is valid, you can use it to send an email, save data in the database, etc.

You can also define a form_invalid method and handle invalid forms yourself (the default behavior is to go back to the page that sent the form, and display the errors).

If you want to send an email, you can use the included send_dgpf_form_by_email method, which needs a few more vars defined in your form class.

You can also save the object in your database using the included save_dgpf_form method.

from django_general_purpose_forms.forms import AbstractGeneralPurposeForm

class MyForm(AbstractGeneralPurposeForm):
    form_name = "key"
    # ^^^ this is the name of your form, it's used to retrieve the form from the settings dict (in the submit view)
    first_name = forms.CharField(max_length=100)
    # [...]
    message = forms.CharField(widget=forms.Textarea)


    def save(self):
         # The form is valid, this method is called, do what you want here!
        ...

Display your form in a template

If you have your object available in your template, you can simply use its get_dgpf_form method to get a form instance, and display it:

{{ my_object.get_dgpf_form.as_p }}

The form_name attribute is also used here; it's added in the url of the form (using action=""), and it's used in DGPF HandleFormView to retrieve the form from the settings dict:

<form method="post" action="{% url 'django_general_purpose_forms:handle_form_submission' form_name=my_object.get_dgpf_form.form_name %}">

Customize template used for displaying errors

The template used to display errors is located in templates/django_general_purpose_forms/form.html. It shows the current form, with the errors, and that's it (in fact it does not even include a <html> tag).

You can (must?) override it in your project and customize it (add your header/footer/custom css, etc.).

Example

Here's what a real-world implementation would look like in your project:

myproject/settings.py

DJANGO_GENERAL_PURPOSE_FORMS_CONFIG = {
    "activity": {
        "form_path": "activity.forms.ActivityContactForm",
        "success_url": "catalog:activity_detail",
    },
}

myproject/activity/models.py

from .forms import ActivityContactForm

class Activity(models.Model):
    name = models.CharField(
        verbose_name="Name",
        max_length=255,
    )
    # [...]

    def get_dgpf_form(self):
        return ActivityContactForm(instance=self)

myproject/activity/forms.py

from django_general_purpose_forms.forms import AbstractGeneralPurposeForm

class ActivityContactForm(AbstractGeneralPurposeForm):
    form_name = "activity"
    #            ^^^^^^^^ same key than in DJANGO_GENERAL_PURPOSE_FORMS_CONFIG

    # form fields:
    name = forms.CharField(max_length=100)
    message = forms.CharField(widget=forms.Textarea)

    # methods related to send_dgpf_form_by_email
    def get_email_address(self):
        return [self.object.owner.email]

    def get_subject(self):
        return f"You have received an e-mail about {self.object.name}"

    def get_from_email(self):
        return settings.DEFAULT_FROM_EMAIL

    def get_txt_message(self):
        return f"From: {self.cleaned_data["name"]}\nMessage:\n{self.cleaned_data["message"]}"

    def get_html_message(self):
        return f"<pre>{self.get_txt_message()}</pre>"

    # what to do when the form is valid
    def save(self):
        self.send_dgpf_form_by_email()  # this method use get_email_address... get_html_message
        self.save_dgpf_form()  # this one only use get_txt_message

myproject/activity/templates/activity/my_model_detail.html

{# ... page content #}

{% with object.get_dgpf_form as dgpf_form %}
  <form method="post" action="{% url 'django_general_purpose_forms:handle_form_submission' form_name=dgpf_form.form_name %}">
    {{ dgpf_form.as_p }}
    {% csrf_token %}
    <button type="submit">{% translate "Send" %}</button>
  </form>
{% endwith %}

{# page content ... #}

This is a simple “tunnel” implementation that redirects the visitor to a new view when the form is submitted.

You can define a new View or a new Mixin attached to a new url sitting in your app (activity/views.py & activity/urls.py) if you really need to implement this form in a different way.

In order to do this, all you have to do is write your view (take inspiration from django_general_purpose_forms.views.HandleFormView), add a new url pointing to this view in your urls.py, and replace the {% url %} tag in your template with this new url.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_general_purpose_forms-0.0.1.tar.gz (18.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_general_purpose_forms-0.0.1-py3-none-any.whl (21.8 kB view details)

Uploaded Python 3

File details

Details for the file django_general_purpose_forms-0.0.1.tar.gz.

File metadata

  • Download URL: django_general_purpose_forms-0.0.1.tar.gz
  • Upload date:
  • Size: 18.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.0.1 CPython/3.11.9 Linux/6.8.0-57-generic

File hashes

Hashes for django_general_purpose_forms-0.0.1.tar.gz
Algorithm Hash digest
SHA256 1de70becdac0c315247c2d96326161a06089f9a72d7e6418acd51887ae1c6f48
MD5 d58370f8eca868bc8f0f4358ea734f2a
BLAKE2b-256 04cb06c0944dc8551eddd5860489e48db59bf011c9d74b81771093e98e923a19

See more details on using hashes here.

File details

Details for the file django_general_purpose_forms-0.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for django_general_purpose_forms-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 510ae854fc5c593065aadc688f7ff1b797f2068086965c68d3d154335e941f7b
MD5 fd531c5f106b9a291691d657d92b55e5
BLAKE2b-256 58d62235cd40e2a67ec161f9dd31e686e5ea5dfe8db897b0e62b9930ab3e2144

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page