Lookaway App Profile Forms

Written by Kyle Bruder, published on April 5, 2021, 1:40 a.m., last updated on April 5, 2021, 1:52 a.m.


logo


Add the form classes used in the create and update views for the Lookaway CMS AppProfile and AppPageSection models.


Import the Forms, Widgets and Models

foo/forms.py
from django import forms
from django.forms import Textarea
from templates.widgets import ImagePreviewWidget, SoundPreviewWidget, VideoPreviewWidget
from members.models import Member
from objects.models import Image, Sound, Video, Code, Link
from .models import FooAppProfile, FooPageSection

Source: kbruder Tech


Add the Custom Fields

Notice there are 3 classes. The first one is a custom iterator that returns a tuple containing a thrid value; the model instance, where as the standard Django iterator returns a tuple containing only the value and label of each form field. This is so we can use the instance to reference the url of the image in the 'choices' list in order to display it in the form. The other two classes are the standard ModelChoice and ModelMultipleChoice field classes that use our custom iterator instead.

Caution: These fields only work with the custom image choice and image multiple choice widgets provided with Lookaway CMS. You must exclude from {{ form }} them when using Django's build in form field iterator in HTML templates or you will get a "Too many values to unpack" Python error.

foo/forms.py
class CustomModelChoiceIterator(forms.models.ModelChoiceIterator):

    def choice(self, obj):
        return self.field.prepare_value(obj), self.field.label_from_instance(obj), obj

class CustomModelChoiceField(forms.models.ModelChoiceField):

    def _get_choices(self):
        if hasattr(self, '_choices'):
            return self._choices
        return CustomModelChoiceIterator(self)

    choices = property(_get_choices, forms.ChoiceField._set_choices)

class CustomModelMultipleChoiceField(forms.models.ModelMultipleChoiceField):

    def _get_choices(self):
        if hasattr(self, '_choices'):
            return self._choices
        return CustomModelChoiceIterator(self)

    choices = property(_get_choices, forms.MultipleChoiceField._set_choices)

Source: kbruder Tech


Add the AppProfile Form

There is a lot going on in this class. We have defined the fields we want to use in the form, including the logo, banner, and background image which are using the CustomModelChoiceField instead of the radio field, the default for ForeignKey model fields. Then we have updated some of the fields to use the dark mode Lookaway CSS. In the Meta class, we define the fields we want to display, along with some additional help texts. Finally we override the init method in order to ensure that the model choice query sets for ForeignKey and ManyToMany fields only include models that are owned by the user accessing the form.

Caution: Make sure to pass the 'User' Django object with the kwargs in the form view or the form will break.

foo/forms.py
# Foo app profile form
class FooAppProfileForm(forms.ModelForm):

    title = forms.CharField(
        help_text="""The title will appear in the header
            It will also appear on search engine results pages (SERPs) and can \
            impact search engine optimization (SEO)""",
        max_length=128,
        required=False,
    )
    meta_description = forms.CharField(
        widget=forms.Textarea(
            attrs={
                'class': 'form-text-field',
            }
        ),
        help_text="""Add a short description
            The description will be used by Search Engines and will impact SEO
            Include key words used in the title
            Keep it less than 155 characters""",
        max_length=155,
        required=False,
    )
    text = forms.CharField(
        widget=forms.Textarea(
            attrs={
                'class': 'form-text-field',
            }
        ),
        label="Blurb",
        help_text="Add a short blurb that will be displayed under the header",
        max_length=65535,
        required=False,
    )
    logo = CustomModelChoiceField(
        queryset=Image.objects.all(),
        required=False, 
        help_text="""The logo will appear on the landing page and list headers
            The optimal image size is 250 pixels wide by 250 pixels high \
            (1:1)""",
    )
    banner = CustomModelChoiceField(
        queryset=Image.objects.all(),
        required=False, 
        help_text="""The banner is the background image for the landing page \
            header
            The optimal image size is 1800 pixels wide by 400 pixels high \
            (9:2)""",
    )
    bg_image = CustomModelChoiceField(
        queryset=Image.objects.all(),
        required=False, 
        label="Background Image",
        help_text="The background image will appear on pages related to \
            this app",
    )
    n_foos = forms.IntegerField(
        max_value=1000,
        min_value=0,
        label="Number of foos to show in each list on the landing page (n)",
        widget=forms.NumberInput(
            attrs={
                'class': 'form-text-field',
            }
        ),
    )
    n_bars = forms.IntegerField(
        max_value=1000,
        min_value=0,
        label="Number of bars to show in each list on the landing page (n)",
        widget=forms.NumberInput(
            attrs={
                'class': 'form-text-field',
            }
        ),
    )
    foo_list_pagination = forms.IntegerField(
        max_value=1000,
        min_value=1,
        label="Number of foos to show in lists",
        widget=forms.NumberInput(
            attrs={
                'class': 'form-text-field',
            }
        ),
    )
    bar_list_pagination = forms.IntegerField(
        max_value=1000,
        min_value=1,
        label="Number of bars to show in lists",
        widget=forms.NumberInput(
            attrs={
                'class': 'form-text-field',
            }
        ),
    )
    show_new_foos = forms.BooleanField(
        label="Show the n newest foos on the landing page",
        required=False,
    )
    show_top_foos = forms.BooleanField(
        label="Show the top n foos on the landing page",
        required=False,
    )
    show_new_bars = forms.BooleanField(
        label="Show the n newest bars on the landing page",
        required=False,
    )
    show_top_bars = forms.BooleanField(
        label="Show the top n bars on the landing page",
        required=False,
    )
    title.widget.attrs.update({'class': 'form-text-field'})

    class Meta:
        model = FooAppProfile
        fields = (
            'title',
            'show_title',
            'meta_description',
            'show_desc',
            'text',
            'logo',
            'banner',
            'bg_image',
            'n_foos',
            'n_bars',
            'foo_list_pagination',
            'bar_list_pagination',
            'show_new_foos',
            'show_top_foos',
            'show_new_bars',
            'show_top_bars',
            'links',
            'bitcoin_wallet',
            'litecoin_wallet',
        )
        help_texts = {
            'links': "Add featured links that will appear on the landing page",
            'show_title': """Check this option if you would like the title to \
                appear on the landing page header""",
            'show_desc': """Check this option if you would like the \
                meta description to appear on the landing page""",
        }
        labels = {
            'show_desc': "Show description",
        }

    def __init__(self, *args, **kwargs):
        user = kwargs.pop('user')
        super(FooAppProfileForm, self).__init__(*args, **kwargs)
        self.fields['logo'].queryset = Image.objects.filter(
            owner=user.pk,
        ).order_by(
            '-last_modified',
        )
        self.fields['banner'].queryset = Image.objects.filter(
            owner=user.pk,
        ).order_by(
            '-last_modified',
        )
        self.fields['bg_image'].queryset = Image.objects.filter(
            owner=user.pk,
        ).order_by(
            '-last_modified',
        )

Source: kbruder Tech


Add the AppPageSection Form

The AppPageSection class is very similar to the AppProfile class. In this example we are adding the ManyToMany fields we defined in the model.

foo/forms.py
class FooPageSectionForm(forms.ModelForm):

    is_enabled = forms.BooleanField(
        label="Enabled",
        help_text="""Choose this option if you want this section to appear\
            on the landing page""",
        required=False,
    )
    images = CustomModelMultipleChoiceField(
        queryset = Image.objects.all(),
        required=False,
        help_text="Choose one or more Images to include in this Section",
    )
    title = forms.CharField(
        help_text="""The section title will appear in the header of this \
            Section""",
        max_length=255,
    )
    text = forms.CharField(
        widget=forms.Textarea(
            attrs={
                'class': 'form-text-field',
            }
        ),
        help_text="Enter the section text here",
        max_length=65535,
        required=False,
    )
    info = forms.CharField(
        widget=forms.Textarea(
            attrs={
                'class': 'form-text-field',
            }
        ),
        label="Info",
        help_text="Add highlighted information in this section",
        max_length=65535,
        required=False,
    )
    alert = forms.CharField(
        widget=forms.Textarea(
            attrs={
                'class': 'form-text-field',
            }
        ),
        label="Alert",
        help_text="Add a highlighted alert that will display in this section",
        max_length=65535,
        required=False,
    )
    order = forms.DecimalField(
        help_text="""Choose the order in which the section will appear on \
            the landing page
            Lower values will appear first""",
        max_digits=8,
        initial=0,
    )
    hide_title = forms.BooleanField(
        help_text ="""Choose this option if you do not want \
            the title of this section to be displayed on the page""",
        required=False,
    )
    order.widget.attrs.update({'class': 'form-text-field'})
    title.widget.attrs.update({'class': 'form-text-field'})

    class Meta:
        model = FooPageSection
        fields = (
            'is_enabled',
            'members_only',
            'images',
            'title',
            'hide_title',
            'order',
            'text',
            'info',
            'alert',
            'foos',
            'bars',
            'sounds',
            'videos',
            'code',
            'links',
        )
        help_texts = {
            'sounds': """Choose one or more Sounds""",
            'videos': """Choose one or more Videos""",
            'code': """Choose one or more Code samples""",
            'links': """Choose one or more Links""",
            'members_only': """Choose this option if you would like to \
                restrict the visibility of this section to members of \
                the site""",
        }

    def __init__(self, *args, **kwargs):
        user = kwargs.pop('user')
        super(FooPageSectionForm, self).__init__(*args, **kwargs)
        self.fields['images'].queryset = Image.objects.filter(
            owner=user.pk,
        ).order_by(
            '-last_modified',
        )
        self.fields['sounds'].queryset = Sound.objects.filter(
            owner=user.pk,
        ).order_by(
            '-last_modified',
        )
        self.fields['videos'].queryset = Video.objects.filter(
            owner=user.pk,
        ).order_by(
            '-last_modified',
        )
        self.fields['code'].queryset = Code.objects.filter(
            owner=user.pk,
        ).order_by(
            '-last_modified',
        )
        self.fields['foos'].queryset = Foo.objects.filter(
            is_public=True,
        ).order_by(
            '-last_modified',
        )
        self.fields['bars'].queryset = Bar.objects.filter(
            is_public=True,
        ).order_by(
            '-last_modified',
        )

Source: kbruder Tech



Referenced by

App Profile Design Patterns - Lookaway CMS

Lookaway CMS comes with class mixins that can be used when writing new apps. These powerful customization features can extend your models and views.

bitcoin:3MnhNRKgrpTFQWstYicjF6GebY7u7dap4u
Bitcoin Accepted Here

3MnhNRKgrpTFQWstYicjF6GebY7u7dap4u

Please donate some Bitcoin to our site. We can use it to keep improving the site and open up to more members. Any amount will help. Thank you.


litecoin:MT61gm6pdp9rJoLyMWW5A1hnUpxAERnxqg
Litecoin Accepted Here

MT61gm6pdp9rJoLyMWW5A1hnUpxAERnxqg

Please donate some Lite to our site. We can use it to keep improving the site and open it up to more members. Any amount will help. Thank you.


Lookaway: Development Django Python Lookaway: CMS

Contributed by Kyle Bruder on April 5, 2021, 1:40 a.m.
Last updated on April 5, 2021, 1:52 a.m.