I have a website running on Django that uses Wagtail for most of the pages (that are simply text content, editable through the Wagtail Admin UI). However, I have some content that needs to be dynamically rendered from my own Django model (let's say it's a list of handouts). I'd like that content to be rendered on a Wagtail page that would also have some Wagtail models (e.g., top menu, some introductory text). What's the best way to do this? I thought about making the list of handouts an API endpoint, and rendering it with JavaScript + XHR, but it seems like there should be a good way to do it server-side.
Assuming the introductionary text is a block in a Wagtail StreamField, you could also define a Wagtail Block that links to a Django model. https://pypi.org/project/wagtail-modelchooser/ is a useful extension that provides this functionality. This makes it possible to render things from Django model instances in your Wagtail stream content.
One option would be to create a property on your Page model that retrieves whatever you are looking for from this other model
# rest of your imports
...
from handouts import Handouts
class MyPage(Page):
# rest of your page's fields
...
#property
def get_handouts(self):
handouts = Handouts.objects.all()
return handouts
Related
The goal is to create a text file editor within the Wagtail admin site.
Specifically for robots.txt file. I want the admin to update the file from the admin.
How can I do that?
There are some considerations to make for the robots.txt file hitting your server and doing a database read each time. You probably will want to consider some kind of caching layer here, however I have answered assuming you will resolve that and just want to get a Wagtail 'editor' solution here.
Wagtail provides a contrib.settings module that allows you to have a settings model per site. This is a very helpful module that allows users, usually admin only users, to modify content that is specific to a site but does not suit the idea of a 'page'.
https://docs.wagtail.org/en/stable/reference/contrib/settings.html
Settings model
In your app's models.py file - set up a new settings model.
from django.db import models
from wagtail.admin.panels import FieldPanel
from wagtail.contrib.settings.models import BaseSetting, register_setting
#register_setting
class MySettings(BaseSetting):
robots = models.TextField(blank=True)
panels = [
FieldPanel('robots'),
]
robots.txt view & template
There are lots of ways to load a View with robots.txt
Accordbox has a good tutorial on how to set up a robots.txt view https://www.accordbox.com/blog/wagtail-seo-guide/#robotstxt
Once you have your view, you can get the settings output via the model class.
# using in your view
def robots_view(request):
robots = MySettings.for_request(request).robots
## return the content in your view
Alternatively you can use in your template context via an injected template context variable
note: 'wagtail.contrib.settings.context_processors.settings' must be added to context_processors
User-agent: *
{{ settings.app_label.MySettings.robots }}
Considerations
If possible, it would always be better to serve a static file here, or strongly cache this value at a minimum.
Validation is a must - make it hard for your editors to break your website, remember that search engines only crawl this semi-regularly and breaking this could mean a few days of de-listing or broken listings if your editors are not careful.
Permissions are a must, maybe even a two step process to push these changes live somehow?
You could possibly put a robots field on your HomePage model and access the data in your robots view. This kind of breaks the concept of this model only reflecting the home page but then extending to the 'root' content.
I have a model with fields name, roll_no, birth_date
I am using the django admin's list display and list editable to have these fields displayed and edited in a list format in a single page. However, to add a new entry I have to go to the create_form page.
Is it possible to simply add new objects from the list_display page itself?
Unfortunately this feature is not available out-of-the box in the Django admin like the ModelAdmin.list_editable feature.
I'm curious to see if there are other shortcuts, but at the moment the only way I see is to customize the formset like descibed in the official Docs:
from django import forms
class MyForm(forms.ModelForm):
# customize your 'extra' forms here
class MyModelAdmin(admin.ModelAdmin):
def get_changelist_form(self, request, **kwargs):
return MyForm
And finally manually extend the changelist form template of the admin. To override a Django admin template, please follow the intructions in the Official Docs here. The template to be customized is the following folder:
.../django/contrib/admin/templates/admin/change_list.html
and you probably need to override the {% block result_list %} in that file.
NB: the customization of an admin template can be very tricky. Consider to use a CMS (like DjangoCMS) if you need to extend the user experience. The idea behind the Django admin is to make your life easier with an out-of-the-box interface for CRUDs on your DB. IMHO try to avoid complex customizations of the Django Admin if not strictly needed.
Pretty much as the subject has it: I have a Flask-Admin site, and would like to add TinyMCE or CKEditor to a textarea in an inline model.
There are various instructions for adding CKEditor to Flask-Admin in general, e.g. Flask-Admin and CKEditor WYSIWYG textarea integration and Getting CKEditor to work with Flask-Admin. I've followed these to create the WTForms widget and field. The inline models don't use edit.html, for pulling in the JS, but I can add it to the master template, though this is not ideal.
But the main issue is how to attach the WTForms field to the Flask-Admin model. The existing instructions have you set a form_overrides = dict(fieldname=CKTextAreaField). But that doesn't work inside inline_models. How do I accomplish this?
I had this issue a few days ago and managed to solve it by adding this to my view class.
class CustomView(ModelView):
extra_js = ['//cdn.ckeditor.com/4.6.0/standard/ckeditor.js']
inline_models=[ ( YourModel, dict(
form_overrides={
'FieldName':CKTextAreaField
}
))]
Assuming that you followed the flask admin docs tutorial for CKEditor.
inline_models consist of (model, options) pairs. The "options" object is the key here: it accepts most of the form_* attributes that ModelView has.
I want to implement in Django Admin a jquery plugin that "adjust" and image(http://guillotine.js.org/), them get the coordinates with ImageKit and save the new image.
I need some tutorials and advises how to do it.
I have no tutorials, but can give you advice.
You can customize your admin model with custom css and js, by Media class, like so:
class MyModelAdmin(admin.ModelAdmin):
class Media:
css = {
"all": ("my_styles.css",)
}
js = ("my_code.js",)
You can look in dev tools, how Django chose names id's and classes for elements in page and also check the docs.
Admin docs
I'm creating a simple project in Django to further my knowledge of it and Python, essentially just going to store bookmarks and tags, both will have views, and there will be 3 models:
Tag
Bookmark
Bookmark_Tag (relationships)
I read the "Projects vs Apps" panel in the django documentation, but I'm still unsure of how to lay it out, right now it's all within one Bookmark App, should there be a seperate app for Bookmarks and a seperate app for Tags, and if so, where does the model for the relationships live?
Thanks!
No, you don't need a separate app for each. They're closely related, and it sounds like together they define your app, not separately. If later, you added another functionality to your site that used the same database and settings but didn't share much else with your current app, that would be another app in the same project.
See Django project models.py versus app models.py and Django: "projects" vs "apps" on this site as well as Django tips: laying out an application for some more guidelines.
If Bookmarks and Tags have a many-to-many relationship, and you need to add extra fields to that relationship (other than just the ids of the related objects) you can use a ManyToManyField and set the through table:
class Bookmark(models.Model):
# whatever fields you need on Bookmark
tags = models.ManyToManyField('Tag', through = 'BookmarkTag')
class Tag(models.Model):
# whatever fields you need on Tag
pass
class BookmarkTag(models.Model):
bookmark = models.ForeignKey(Bookmark)
tag = models.ForeignKey(Tag)
# whatever additional fields you need on the relationship
See the Django docs on Extra fields on many-to-many relationships for more info.