Django App Upload Docs Make Searchable Admin

Install and run Wagtail¶

Install dependencies¶

Wagtail supports Python three.7, 3.8, 3.9 and 3.10.

To check whether yous take an appropriate version of Python 3:

If this does not return a version number or returns a version lower than three.7, yous volition need to install Python 3.

Important

Earlier installing Wagtail, it is necessary to install the libjpeg and zlib libraries, which provide support for working with JPEG, PNG and GIF images (via the Python Pillow library). The way to do this varies past platform—see Pillow's platform-specific installation instructions.

Create and actuate a virtual surroundings¶

We recommend using a virtual environment, which isolates installed dependencies from other projects. This tutorial uses venv , which is packaged with Python 3.

On Windows (cmd.exe):

                            >              python3 -m venv mysite\env              >              mysite\env\Scripts\activate.bat            

On GNU/Linux or MacOS (fustigate):

                            $              python3 -m venv mysite/env              $                            source              mysite/env/bin/activate            

For other shells see the venv documentation.

Notation

If you're using version command (eastward.k. git), mysite will exist the directory for your projection. The env directory within of it should be excluded from any version control.

Install Wagtail¶

Employ pip, which is packaged with Python, to install Wagtail and its dependencies:

Generate your site¶

Wagtail provides a start control similar to django-admin startproject . Running wagtail start mysite in your project will generate a new mysite folder with a few Wagtail-specific extras, including the required project settings, a "home" app with a blank HomePage model and bones templates, and a sample "search" app.

Because the folder mysite was already created by venv , run wagtail start with an additional argument to specify the destination directory:

                            $              wagtail first mysite mysite            

Notation

Mostly, in Wagtail, each page blazon, or content type, is represented past a single app. However, different apps tin can be aware of each other and access each other'due south data. All of the apps need to exist registered within the INSTALLED_APPS section of the settings file. Expect at this file to run into how the offset control has listed them in at that place.

Install project dependencies¶

                            $                            cd              mysite              $              pip install -r requirements.txt            

This ensures that yous accept the relevant versions of Wagtail, Django, and any other dependencies for the project you take just created.

Create the database¶

If you oasis't updated the project settings, this volition be a SQLite database file in the projection directory.

                            $              python manage.py migrate            

This command ensures that the tables in your database are matched to the models in your project. Every time you alter your model (eg. yous may add a field to a model) you will demand to run this command in order to update the database.

Create an admin user¶

                            $              python manage.py createsuperuser            

When logged into the admin site, a superuser has full permissions and is able to view/create/manage the database.

Extend the HomePage model¶

Out of the box, the "home" app defines a bare HomePage model in models.py , along with a migration that creates a homepage and configures Wagtail to use it.

Edit home/models.py as follows, to add a body field to the model:

                        from            django.db            import            models            from            wagtail.core.models            import            Page            from            wagtail.cadre.fields            import            RichTextField            from            wagtail.admin.edit_handlers            import            FieldPanel            class            HomePage            (            Page            ):            body            =            RichTextField            (            blank            =            True            )            content_panels            =            Page            .            content_panels            +            [            FieldPanel            (            'body'            ,            classname            =            "full"            ),            ]          

torso is defined as RichTextField , a special Wagtail field. When blank=True , it means that this field is not required and can be empty. Yous tin can utilise any of the Django core fields. content_panels define the capabilities and the layout of the editing interface. When you add together fields to content_panels , information technology enables them to be edited on the Wagtail interface. More on creating Page models.

Run python manage.py makemigrations (this will create the migrations file), so python manage.py migrate (this executes the migrations and updates the database with your model changes). You must run the above commands each time you lot make changes to the model definition.

You can now edit the homepage within the Wagtail admin area (go to Pages, Homepage, then Edit) to meet the new trunk field. Enter some text into the torso field, and publish the page past selecting Publish at the bottom of the folio editor, rather than Save Draft.

The page template now needs to be updated to reflect the changes made to the model. Wagtail uses normal Django templates to render each page blazon. By default, it will wait for a template filename formed from the app and model name, separating majuscule letters with underscores (east.g. HomePage within the 'home' app becomes habitation/home_page.html ). This template file can be in any location recognised past Django's template rules; conventionally it is placed under a templates folder inside the app.

Edit home/templates/home/home_page.html to comprise the post-obit:

                        {%            extends            "base of operations.html"            %}            {%            load            wagtailcore_tags            %}            {%            block            body_class            %}template-homepage{%            endblock            %}            {%            cake            content            %}            {{            page.body            |            richtext            }}            {%            endblock            %}          

base of operations.html refers to a parent template and must always be the first template tag used in a template. Extending from this template saves you from rewriting code and allows pages across your app to share a similar frame (by using cake tags in the kid template, you are able to override specific content within the parent template).

wagtailcore_tags must also be loaded at the summit of the template and provide additional tags to those provided past Django.

../_images/tutorial_3.png

A basic weblog¶

We are at present gear up to create a blog. To exercise so, run python manage.py startapp blog to create a new app in your Wagtail site.

Add together the new blog app to INSTALLED_APPS in mysite/settings/base of operations.py .

Blog Index and Posts¶

Lets start with a simple index page for our blog. In blog/models.py :

                            from              wagtail.core.models              import              Page              from              wagtail.core.fields              import              RichTextField              from              wagtail.admin.edit_handlers              import              FieldPanel              course              BlogIndexPage              (              Folio              ):              intro              =              RichTextField              (              blank              =              True              )              content_panels              =              Folio              .              content_panels              +              [              FieldPanel              (              'intro'              ,              classname              =              "full"              )              ]            

Run python manage.py makemigrations and python manage.py migrate .

Since the model is called BlogIndexPage , the default template name (unless we override it) will exist blog/templates/weblog/blog_index_page.html . Create this file with the following content:

                            {%              extends              "base.html"              %}              {%              load              wagtailcore_tags              %}              {%              cake              body_class              %}template-blogindexpage{%              endblock              %}              {%              block              content              %}              <              h1              >              {{              page.title              }}              </              h1              >              <              div              course              =              "intro"              >              {{              page.intro              |              richtext              }}              </              div              >              {%              for              mail service              in              page.get_children              %}              <              h2              ><              a              href              =              "              {%              pageurl              post              %}              "              >              {{              post.championship              }}              </              a              ></              h2              >              {{              post.specific.intro              }}              {{              mail.specific.body              |              richtext              }}              {%              endfor              %}              {%              endblock              %}            

Most of this should be familiar, but we'll explain get_children a fleck subsequently. Note the pageurl tag, which is similar to Django's url tag just takes a Wagtail Page object equally an statement.

In the Wagtail admin, create a BlogIndexPage as a child of the Homepage, make certain it has the slug "blog" on the Promote tab, and publish information technology. You should now be able to access the url /blog on your site (note how the slug from the Promote tab defines the folio URL).

At present we need a model and template for our blog posts. In blog/models.py :

                            from              django.db              import              models              from              wagtail.core.models              import              Page              from              wagtail.core.fields              import              RichTextField              from              wagtail.admin.edit_handlers              import              FieldPanel              from              wagtail.search              import              index              # Proceed the definition of BlogIndexPage, and add:              class              BlogPage              (              Page              ):              engagement              =              models              .              DateField              (              "Mail engagement"              )              intro              =              models              .              CharField              (              max_length              =              250              )              body              =              RichTextField              (              blank              =              Truthful              )              search_fields              =              Page              .              search_fields              +              [              index              .              SearchField              (              'intro'              ),              index              .              SearchField              (              'torso'              ),              ]              content_panels              =              Page              .              content_panels              +              [              FieldPanel              (              'engagement'              ),              FieldPanel              (              'intro'              ),              FieldPanel              (              'trunk'              ,              classname              =              "full"              ),              ]            

In the model above, nosotros import index as this makes the model searchable. You tin can so listing fields that you desire to be searchable for the user.

Run python manage.py makemigrations and python manage.py migrate .

Create a template at blog/templates/blog/blog_page.html :

                            {%              extends              "base.html"              %}              {%              load              wagtailcore_tags              %}              {%              cake              body_class              %}template-blogpage{%              endblock              %}              {%              block              content              %}              <              h1              >              {{              page.title              }}              </              h1              >              <              p              form              =              "meta"              >              {{              folio.date              }}              </              p              >              <              div              class              =              "intro"              >              {{              folio.intro              }}              </              div              >              {{              page.body              |              richtext              }}              <              p              ><              a              href              =              "              {{              page.get_parent.url              }}              "              >Render to blog</              a              ></              p              >              {%              endblock              %}            

Note the use of Wagtail's congenital-in get_parent() method to obtain the URL of the blog this mail service is a function of.

Now create a few weblog posts as children of BlogIndexPage . Exist sure to select type "Web log Page" when creating your posts.

../_images/tutorial_4a.png

../_images/tutorial_4b.png

Wagtail gives you total control over what kinds of content can be created under various parent content types. By default, whatsoever page type can exist a child of any other page type.

../_images/tutorial_5.png

Publish each blog postal service when you lot are washed editing.

You should now have the very beginnings of a working weblog. Access the /blog URL and you should see something like this:

../_images/tutorial_7.png

Titles should link to post pages, and a link back to the blog's homepage should appear in the footer of each post page.

Parents and Children¶

Much of the work you'll be doing in Wagtail revolves around the concept of hierarchical "tree" structures consisting of nodes and leaves (come across Theory). In this instance, the BlogIndexPage is a "node" and private BlogPage instances are the "leaves".

Take another look at the guts of blog_index_page.html :

                            {%              for              postal service              in              page.get_children              %}              <              h2              ><              a              href              =              "              {%              pageurl              postal service              %}              "              >              {{              post.championship              }}              </              a              ></              h2              >              {{              post.specific.intro              }}              {{              post.specific.body              |              richtext              }}              {%              endfor              %}            

Every "folio" in Wagtail can telephone call out to its parent or children from its own position in the hierarchy. But why practice we have to specify postal service.specific.intro rather than mail service.intro ? This has to do with the way we divers our model:

course BlogPage(Page):

The get_children() method gets us a list of instances of the Page base class. When we want to reference backdrop of the instances that inherit from the base class, Wagtail provides the specific method that retrieves the actual BlogPage tape. While the "title" field is nowadays on the base Page model, "intro" is just present on the BlogPage model, and so we need .specific to access it.

To tighten up template lawmaking like this, nosotros could use Django's with tag:

                            {%              for              post              in              page.get_children              %}              {%              with              mail              =              post.specific              %}              <              h2              ><              a              href              =              "              {%              pageurl              post              %}              "              >              {{              post.title              }}              </              a              ></              h2              >              <              p              >              {{              post.intro              }}              </              p              >              {{              mail.body              |              richtext              }}              {%              endwith              %}              {%              endfor              %}            

When you start writing more customized Wagtail lawmaking, you'll observe a whole set of QuerySet modifiers to aid you navigate the hierarchy.

                            # Given a page object 'somepage':              MyModel              .              objects              .              descendant_of              (              somepage              )              child_of              (              folio              )              /              not_child_of              (              somepage              )              ancestor_of              (              somepage              )              /              not_ancestor_of              (              somepage              )              parent_of              (              somepage              )              /              not_parent_of              (              somepage              )              sibling_of              (              somepage              )              /              not_sibling_of              (              somepage              )              # ... and ...              somepage              .              get_children              ()              somepage              .              get_ancestors              ()              somepage              .              get_descendants              ()              somepage              .              get_siblings              ()            

For more information, encounter: Page QuerySet reference

Overriding Context¶

There are a couple of problems with our blog index view:

  1. Blogs generally display content in reverse chronological order
  2. We want to brand sure we're only displaying published content.

To accomplish these things, nosotros demand to do more than simply catch the index page'southward children in the template. Instead, we'll desire to modify the QuerySet in the model definition. Wagtail makes this possible via the overridable get_context() method. Alter your BlogIndexPage model like this:

                            class              BlogIndexPage              (              Folio              ):              intro              =              RichTextField              (              blank              =              True              )              def              get_context              (              cocky              ,              request              ):              # Update context to include but published posts, ordered by contrary-chron              context              =              super              ()              .              get_context              (              asking              )              blogpages              =              cocky              .              get_children              ()              .              live              ()              .              order_by              (              '-first_published_at'              )              context              [              'blogpages'              ]              =              blogpages              return              context            

All we've done here is retrieve the original context, create a custom QuerySet, add it to the retrieved context, and return the modified context dorsum to the view. You'll too demand to modify your blog_index_page.html template slightly. Change:

{% for postal service in folio.get_children %} to {% for mail in blogpages %}

Now effort unpublishing one of your posts - it should disappear from the weblog index page. The remaining posts should now be sorted with the most recently published posts offset.

Images¶

Let'southward add together the power to attach an image gallery to our blog posts. While information technology's possible to merely insert images into the body rich text field, there are several advantages to setting up our gallery images as a new dedicated object blazon within the database - this manner, yous have full control of the layout and styling of the images on the template, rather than having to lay them out in a particular style inside the rich text field. It likewise makes it possible for the images to exist used elsewhere, independently of the blog text - for example, displaying a thumbnail on the blog index page.

Add a new BlogPageGalleryImage model to models.py :

                            from              django.db              import              models              # New imports added for ParentalKey, Orderable, InlinePanel, ImageChooserPanel              from              modelcluster.fields              import              ParentalKey              from              wagtail.cadre.models              import              Page              ,              Orderable              from              wagtail.core.fields              import              RichTextField              from              wagtail.admin.edit_handlers              import              FieldPanel              ,              InlinePanel              from              wagtail.images.edit_handlers              import              ImageChooserPanel              from              wagtail.search              import              index              # ... (Proceed the definition of BlogIndexPage, and update BlogPage:)              class              BlogPage              (              Folio              ):              date              =              models              .              DateField              (              "Post date"              )              intro              =              models              .              CharField              (              max_length              =              250              )              body              =              RichTextField              (              bare              =              True              )              search_fields              =              Folio              .              search_fields              +              [              index              .              SearchField              (              'intro'              ),              index              .              SearchField              (              'body'              ),              ]              content_panels              =              Page              .              content_panels              +              [              FieldPanel              (              'date'              ),              FieldPanel              (              'intro'              ),              FieldPanel              (              'body'              ,              classname              =              "full"              ),              InlinePanel              (              'gallery_images'              ,              label              =              "Gallery images"              ),              ]              class              BlogPageGalleryImage              (              Orderable              ):              folio              =              ParentalKey              (              BlogPage              ,              on_delete              =              models              .              CASCADE              ,              related_name              =              'gallery_images'              )              image              =              models              .              ForeignKey              (              'wagtailimages.Image'              ,              on_delete              =              models              .              Pour              ,              related_name              =              '+'              )              caption              =              models              .              CharField              (              bare              =              True              ,              max_length              =              250              )              panels              =              [              ImageChooserPanel              (              'image'              ),              FieldPanel              (              'caption'              ),              ]            

Run python manage.py makemigrations and python manage.py drift .

There are a few new concepts here, then let'due south take them ane at a time:

Inheriting from Orderable adds a sort_order field to the model, to keep runway of the ordering of images in the gallery.

The ParentalKey to BlogPage is what attaches the gallery images to a specific page. A ParentalKey works similarly to a ForeignKey , but also defines BlogPageGalleryImage equally a "kid" of the BlogPage model, then that it'due south treated as a fundamental part of the page in operations like submitting for moderation, and tracking revision history.

image is a ForeignKey to Wagtail's congenital-in Prototype model, where the images themselves are stored. This comes with a defended panel type, ImageChooserPanel , which provides a pop-upwards interface for choosing an existing image or uploading a new one. This style, we let an image to exist in multiple galleries - finer, we've created a many-to-many relationship between pages and images.

Specifying on_delete=models.Cascade on the foreign key ways that if the image is deleted from the system, the gallery entry is deleted as well. (In other situations, information technology might be appropriate to get out the entry in identify - for example, if an "our staff" page included a list of people with headshots, and one of those photos was deleted, we'd rather leave the person in place on the page without a photo. In this case, we'd set the foreign key to blank=True, null=Truthful, on_delete=models.SET_NULL .)

Finally, adding the InlinePanel to BlogPage.content_panels makes the gallery images bachelor on the editing interface for BlogPage .

Adapt your blog page template to include the images:

                            {%              extends              "base.html"              %}              {%              load              wagtailcore_tags              wagtailimages_tags              %}              {%              block              body_class              %}template-blogpage{%              endblock              %}              {%              block              content              %}              <              h1              >              {{              page.title              }}              </              h1              >              <              p              course              =              "meta"              >              {{              page.date              }}              </              p              >              <              div              class              =              "intro"              >              {{              page.intro              }}              </              div              >              {{              page.body              |              richtext              }}              {%              for              item              in              page.gallery_images.all              %}              <              div              style              =              "float: left; margin: 10px"              >              {%              image              item.epitome              fill-320x240              %}              <              p              >              {{              item.caption              }}              </              p              >              </              div              >              {%              endfor              %}              <              p              ><              a              href              =              "              {{              page.get_parent.url              }}              "              >Return to blog</              a              ></              p              >              {%              endblock              %}            

Hither we utilise the {% image %} tag (which exists in the wagtailimages_tags library, imported at the pinnacle of the template) to insert an <img> element, with a fill-320x240 parameter to indicate that the image should be resized and cropped to fill a 320x240 rectangle. You tin read more almost using images in templates in the docs.

../_images/tutorial_6.jpg

Since our gallery images are database objects in their own right, we tin now query and re-use them independently of the blog post body. Allow's define a main_image method, which returns the paradigm from the first gallery detail (or None if no gallery items be):

                            class              BlogPage              (              Page              ):              date              =              models              .              DateField              (              "Mail service date"              )              intro              =              models              .              CharField              (              max_length              =              250              )              body              =              RichTextField              (              blank              =              True              )              def              main_image              (              self              ):              gallery_item              =              self              .              gallery_images              .              kickoff              ()              if              gallery_item              :              return              gallery_item              .              image              else              :              return              None              search_fields              =              Folio              .              search_fields              +              [              index              .              SearchField              (              'intro'              ),              index              .              SearchField              (              'body'              ),              ]              content_panels              =              Page              .              content_panels              +              [              FieldPanel              (              'date'              ),              FieldPanel              (              'intro'              ),              FieldPanel              (              'body'              ,              classname              =              "full"              ),              InlinePanel              (              'gallery_images'              ,              label              =              "Gallery images"              ),              ]            

This method is at present available from our templates. Update blog_index_page.html to include the master image as a thumbnail alongside each post:

                            {%              load              wagtailcore_tags              wagtailimages_tags              %}              ...              {%              for              post              in              blogpages              %}              {%              with              postal service              =              mail service.specific              %}              <              h2              ><              a              href              =              "              {%              pageurl              mail service              %}              "              >              {{              post.title              }}              </              a              ></              h2              >              {%              with              post.main_image              as              main_image              %}              {%              if              main_image              %}{%              image              main_image              fill-160x100              %}{%              endif              %}              {%              endwith              %}              <              p              >              {{              post.intro              }}              </              p              >              {{              post.torso              |              richtext              }}              {%              endwith              %}              {%              endfor              %}            

Tagging Posts¶

Let'due south say nosotros want to let editors "tag" their posts, and then that readers can, e.thousand., view all bike-related content together. For this, we'll need to invoke the tagging organization arranged with Wagtail, attach it to the BlogPage model and content panels, and render linked tags on the blog post template. Of form, we'll need a working tag-specific URL view every bit well.

Showtime, modify models.py once again:

                            from              django.db              import              models              # New imports added for ClusterTaggableManager, TaggedItemBase, MultiFieldPanel              from              modelcluster.fields              import              ParentalKey              from              modelcluster.contrib.taggit              import              ClusterTaggableManager              from              taggit.models              import              TaggedItemBase              from              wagtail.core.models              import              Page              ,              Orderable              from              wagtail.core.fields              import              RichTextField              from              wagtail.admin.edit_handlers              import              FieldPanel              ,              InlinePanel              ,              MultiFieldPanel              from              wagtail.images.edit_handlers              import              ImageChooserPanel              from              wagtail.search              import              alphabetize              # ... (Keep the definition of BlogIndexPage)              class              BlogPageTag              (              TaggedItemBase              ):              content_object              =              ParentalKey              (              'BlogPage'              ,              related_name              =              'tagged_items'              ,              on_delete              =              models              .              Cascade              )              class              BlogPage              (              Folio              ):              engagement              =              models              .              DateField              (              "Post date"              )              intro              =              models              .              CharField              (              max_length              =              250              )              trunk              =              RichTextField              (              bare              =              True              )              tags              =              ClusterTaggableManager              (              through              =              BlogPageTag              ,              blank              =              True              )              # ... (Keep the main_image method and search_fields definition)              content_panels              =              Page              .              content_panels              +              [              MultiFieldPanel              ([              FieldPanel              (              'date'              ),              FieldPanel              (              'tags'              ),              ],              heading              =              "Blog information"              ),              FieldPanel              (              'intro'              ),              FieldPanel              (              'body'              ),              InlinePanel              (              'gallery_images'              ,              label              =              "Gallery images"              ),              ]            

Run python manage.py makemigrations and python manage.py migrate .

Note the new modelcluster and taggit imports, the add-on of a new BlogPageTag model, and the addition of a tags field on BlogPage . We've besides taken the opportunity to use a MultiFieldPanel in content_panels to group the date and tags fields together for readability.

Edit i of your BlogPage instances, and you should now exist able to tag posts:

../_images/tutorial_8.png

To render tags on a BlogPage , add this to blog_page.html :

                            {%              if              page.tags.all.count              %}              <              div              course              =              "tags"              >              <              h3              >Tags</              h3              >              {%              for              tag              in              page.tags.all              %}              <              a              href              =              "              {%              slugurl              'tags'              %}              ?tag=              {{              tag              }}              "              ><              push              type              =              "push button"              >              {{              tag              }}              </              push button              ></              a              >              {%              endfor              %}              </              div              >              {%              endif              %}            

Notice that we're linking to pages hither with the builtin slugurl tag rather than pageurl , which nosotros used earlier. The divergence is that slugurl takes a Page slug (from the Promote tab) as an argument. pageurl is more commonly used because it is unambiguous and avoids extra database lookups. Simply in the case of this loop, the Page object isn't readily available, and then we fall back on the less-preferred slugurl tag.

Visiting a blog mail with tags should now bear witness a gear up of linked buttons at the bottom - one for each tag. However, clicking a button will get you a 404, since we oasis't yet defined a "tags" view. Add to models.py :

                            course              BlogTagIndexPage              (              Folio              ):              def              get_context              (              self              ,              request              ):              # Filter past tag              tag              =              asking              .              Go              .              become              (              'tag'              )              blogpages              =              BlogPage              .              objects              .              filter              (              tags__name              =              tag              )              # Update template context              context              =              super              ()              .              get_context              (              request              )              context              [              'blogpages'              ]              =              blogpages              return              context            

Note that this Page-based model defines no fields of its own. Even without fields, subclassing Page makes it a part of the Wagtail ecosystem, then that you can requite it a title and URL in the admin, and so that yous tin dispense its contents by returning a QuerySet from its get_context() method.

Migrate this in, and so create a new BlogTagIndexPage in the admin. You'll probably want to create the new page/view as a child of Homepage, parallel to your Weblog alphabetize. Give information technology the slug "tags" on the Promote tab.

Access /tags and Django will tell you what you probably already knew: yous need to create a template blog/blog_tag_index_page.html :

                            {%              extends              "base of operations.html"              %}              {%              load              wagtailcore_tags              %}              {%              block              content              %}              {%              if              request.GET.tag              %}              <              h4              >Showing pages tagged "{{              request.GET.tag              }}"</              h4              >              {%              endif              %}              {%              for              blogpage              in              blogpages              %}              <              p              >              <              strong              ><              a              href              =              "              {%              pageurl              blogpage              %}              "              >              {{              blogpage.championship              }}              </              a              ></              strong              ><              br              />              <              small              >Revised:              {{              blogpage.latest_revision_created_at              }}              </              pocket-size              ><              br              />              {%              if              blogpage.writer              %}              <              p              >By              {{              blogpage.author.profile              }}              </              p              >              {%              endif              %}              </              p              >              {%              empty              %}              No pages constitute with that tag.              {%              endfor              %}              {%              endblock              %}            

Nosotros're calling the built-in latest_revision_created_at field on the Page model - handy to know this is e'er available.

We oasis't notwithstanding added an "author" field to our BlogPage model, nor do we take a Profile model for authors - nosotros'll leave those every bit an do for the reader.

Clicking the tag button at the bottom of a BlogPost should now return a folio something like this:

../_images/tutorial_9.png

Categories¶

Let'due south add a category system to our blog. Unlike tags, where a page author can bring a tag into existence simply by using it on a page, our categories will be a stock-still list, managed by the site owner through a separate area of the admin interface.

Outset, we define a BlogCategory model. A category is non a page in its own right, and so we define it equally a standard Django models.Model rather than inheriting from Page . Wagtail introduces the concept of "snippets" for reusable pieces of content that need to exist managed through the admin interface, but practise not exist equally part of the folio tree themselves; a model can be registered as a snippet by adding the @register_snippet decorator. All the field types nosotros've used so far on pages can be used on snippets as well - here nosotros'll give each category an icon epitome every bit well as a name. Add together to web log/models.py :

                            from              wagtail.snippets.models              import              register_snippet              @register_snippet              class              BlogCategory              (              models              .              Model              ):              proper noun              =              models              .              CharField              (              max_length              =              255              )              icon              =              models              .              ForeignKey              (              'wagtailimages.Image'              ,              nix              =              True              ,              blank              =              True              ,              on_delete              =              models              .              SET_NULL              ,              related_name              =              '+'              )              panels              =              [              FieldPanel              (              'proper name'              ),              ImageChooserPanel              (              'icon'              ),              ]              def              __str__              (              cocky              ):              return              self              .              name              class              Meta              :              verbose_name_plural              =              'weblog categories'            

Notation

Annotation that we are using panels rather than content_panels hither - since snippets mostly accept no need for fields such as slug or publish date, the editing interface for them is not divide into divide 'content' / 'promote' / 'settings' tabs equally standard, and so there is no need to distinguish between 'content panels' and 'promote panels'.

Migrate this change in, and create a few categories through the Snippets area which now appears in the admin menu.

Nosotros can now add categories to the BlogPage model, as a many-to-many field. The field blazon we utilise for this is ParentalManyToManyField - this is a variant of the standard Django ManyToManyField which ensures that the chosen objects are correctly stored against the page record in the revision history, in much the same way that ParentalKey replaces ForeignKey for ane-to-many relations.

                            # New imports added for forms and ParentalManyToManyField              from              django              import              forms              from              django.db              import              models              from              modelcluster.fields              import              ParentalKey              ,              ParentalManyToManyField              from              modelcluster.contrib.taggit              import              ClusterTaggableManager              from              taggit.models              import              TaggedItemBase              # ...              class              BlogPage              (              Page              ):              appointment              =              models              .              DateField              (              "Mail date"              )              intro              =              models              .              CharField              (              max_length              =              250              )              trunk              =              RichTextField              (              blank              =              True              )              tags              =              ClusterTaggableManager              (              through              =              BlogPageTag              ,              blank              =              True              )              categories              =              ParentalManyToManyField              (              'blog.BlogCategory'              ,              blank              =              Truthful              )              # ... (Keep the main_image method and search_fields definition)              content_panels              =              Folio              .              content_panels              +              [              MultiFieldPanel              ([              FieldPanel              (              'engagement'              ),              FieldPanel              (              'tags'              ),              FieldPanel              (              'categories'              ,              widget              =              forms              .              CheckboxSelectMultiple              ),              ],              heading              =              "Blog data"              ),              FieldPanel              (              'intro'              ),              FieldPanel              (              'torso'              ),              InlinePanel              (              'gallery_images'              ,              label              =              "Gallery images"              ),              ]            

Hither we're making use of the widget keyword statement on the FieldPanel definition to specify a checkbox-based widget instead of the default multiple select box, as this is often considered more user-friendly.

Finally, we can update the blog_page.html template to brandish the categories:

                            <              h1              >              {{              page.title              }}              </              h1              >              <              p              class              =              "meta"              >              {{              page.engagement              }}              </              p              >              {%              with              categories              =              page.categories.all              %}              {%              if              categories              %}              <              h3              >Posted in:</              h3              >              <              ul              >              {%              for              category              in              categories              %}              <              li              fashion              =              "display: inline"              >              {%              prototype              category.icon              fill-32x32              fashion              =              "vertical-align: centre"              %}              {{              category.name              }}              </              li              >              {%              endfor              %}              </              ul              >              {%              endif              %}              {%              endwith              %}            

../_images/tutorial_10.jpg

russellhons1987.blogspot.com

Source: https://docs.wagtail.org/en/stable/getting_started/tutorial.html

0 Response to "Django App Upload Docs Make Searchable Admin"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel