I'm trying to extend the flatpage django app to include some bootstrap carousel functionality.
At this point i created a new django app called "carousel" and inside model.py i define a model class called "CarouselImage" that have an ImageField to upload the carousel images and a ForeignKey for the FlatPage.
model.py
from django.contrib.flatpages.models import FlatPage
class CarouselImage(models.Model):
image = models.ImageField(upload_to='carousel/')
page = models.ForeignKey(FlatPage)
timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
active = models.BooleanField(default=True)
def __str__(self):
return str(self.image)
At the carousel/admin.py i'm not having any problem, as you will see in this image 1.
admin.py
from django.contrib.flatpages.admin import FlatPageAdmin
from django.contrib.flatpages.models import FlatPage
from .models import CarouselImage
class CarouselInline(admin.TabularInline):
model = CarouselImage
extra = 1
allow_add = True
class FlatPageAdminWithCarousel(FlatPageAdmin):
inlines = [CarouselInline]
admin.site.unregister(FlatPage)
admin.site.register(FlatPage, FlatPageAdminWithCarousel)
Problem
After all, what i need to know is how i can pass this images to the template?. The problem is that FlatPage has already a view, so i can't create a view to pass those images to the template. I don't know if there is a way to extend a view to include another context.
One Stackoverflow QA pointed to the creation of templatetags, so i tried to create some template tags but if i can't pass those images as a context to the template the templatetags wont work.
templatetags/add_carousel.py
from django import template
register = template.Library()
#register.simple_tag(takes_context=True)
def carousel(context):
image = context['image']
return str(image)
Thanks for all the help you can give,
Iván
You've missed the point of the template tags advice. The point is not to pass images from the context into the tag, but the other way round: query the images in the tag and then pass them into the context from there.
The best way to do this is to use an inclusion tag rather than a simple tag: an inclusion tag is associated with its own template fragment which it renders with its own context, so you can include all elements related to your carousel there, and from the full flatpage template you only need to call the tag itself.
Although actually now I look at your question again, I realise that you don't even need to do this. You have a foreign key from flatpage to image: you can get the images for a page with page.carouselimage_set.all. That makes the template tag irrelevant.
Related
I have a model that I only want to use one row of its table. So, on admin, I would like to remove the list and add pages, and only edit the existing object. The model is this:
from django.db import models
class Banner(models.Model):
pass
class BannerImg(models.Model):
img = models.ImageField(upload_to="banners")
banner = models.ForeignKey(Banner, on_delete=models.CASCADE)
Basically, the Banner(pk=1) will be loaded by the frontend to display a landing page hero slider. I want multiple images, but also want them to be on the same admin form, so one could order, add or remove images from the same place. Of course having to Banner objects, wouldn't make sense in this case.
I can use inline fields to do the form, but how can I achieve the pages functionality (going directly to edit)?
Thanks!
Accordion to documentation.
from django.contrib import admin
class BannerImgInline(admin.TabularInline):
model = BannerImg
class BannerAdmin(admin.ModelAdmin):
inlines = [
BannerImgInline,
]
Here, by dynamic I mean, I wouldn't want to update my template in order for me to update changes. I'd want them to edit in the admin page on my production site. At first, I thought I'd create a model for "About Me" itself, but then I'd need to create a model for just one instance.
I need help with this, better ways to edit my pages dynamically on the admin site.
Perhaps you could create a model for About Me as you mentioned. Since you've highlighted that you would want to work with the django admin site, then what you could do is to set the permission for only one object to be created for that model which can be updated whenever.
For example:
models.py file.
class AboutMe(models.Model):
# With the desired fields of your choice
Now you can set the permission within the admin.py file to only allow one instance from the model to be created.
from django.contrib import admin
from .models import AboutMe
MAX_OBJECTS = 1
# Using decorator here
#admin.register(AboutMe)
class AboutMeAdmin(admin.ModelAdmin):
fields = ['..', '..', '..'] # fields you want to display on the forms
list_display = ['..', '..', '..'] # fields you want to display on the page for list on objects
# Allowing the user to only add one object for this model...
def has_add_permission(self, request):
if self.model.objects.count() >= MAX_OBJECTS:
return False
return super().has_add_permission(request)
That should be a nice fit for your situation. Also, you can read the docs to learn more about customizing django admin site.
I use django-photologue and extended it:
# gallery.models.py
from photologue.models import Photo
from profiles.models import UserProfile
class PhotoExtended(Photo):
user = models.ForeignKey(UserProfile, verbose_name=_('user'), on_delete=models.CASCADE)
# gallery.admin.py
from photologue.admin import PhotoAdmin as PhotoAdminDefault
from photologue.models import Photo
from .models import PhotoExtended
class PhotoAdmin(PhotoAdminDefault):
save_on_top = True
admin.site.unregister(Photo)
admin.site.register(PhotoExtended, PhotoAdmin)
Photologue has a feature to upload zip file with photos and it can be done using additional button in admin. After my changes this button disappeared.
Is it possible to use native photologues admin templates in order to avoid copy-pasting them to my app's template folder? In INSTALLED_APPS photologue is higher than my gallery app
Here there are the photologues admin templates.
In the path templates/admin/photologue/photo/change_list.htmlthe part photo corresponds to the Photo model. You subclassed that model. The new model name is PhotoExtended but there is no template in templates/admin/photologue/photo_extend/change_list.html.
Copy the change_list.html from photologue app into your own (app) template folder. Eg: project/app/templates/admin/photologue/photo_extend/change_list.html.
Alternatively you can also just create a new file and use include the old template:
# project/app/templates/admin/photologue/photo_extend/change_list.html
{% include 'admin/photologue/photo/change_list.html' %}
Update: Another alternative is to set (one of) the BaseModelAdmin properties:
# Custom templates (designed to be over-ridden in subclasses)
add_form_template = None
change_form_template = None
change_list_template = None
delete_confirmation_template = None
delete_selected_confirmation_template = None
object_history_template = None
Here's a piece of Django admin interface's instance edition form:
How should I change the underlying admin.ModelAdmin instance to make it contain an URL, like this?
Django makes this easy. Subclass ModelAdmin, add a custom method and then tell the Admin how to use it. Here's a sample admin.py:
from django.contrib import admin
from .models import Vendor
class VendorAdmin(admin.ModelAdmin):
readonly_fields = ['example_link']
def example_link(self, obj):
return 'link text'.format(obj.get_link()) # however you generate the link
example_link.allow_tags = True
admin.site.register(Vendor, VendorAdmin)
Here's the documentation that furthers explains readonly_fields, customizing the form label text with short_description, ordering, and how you can put this custom url method on the Model or ModelAdmin.
I have a portfolio application that lists projects and their detail pages. Every project generally has the same information (gallery, about etc. ), but sometimes the user might want to add extra information for a particularly large project, maybe an extra page about the funding of that page for example.
Would it be possible to create an overwritten flatpages model within my portfolio app that forces any flatpages created within that application to always begin with /portfolio/project-name/flat-page. I could then simply pass the links to those flatpages associated with the project to the template so any flatpage the user generates will automatically be linked to from the project page.
EDIT
I have it somewhat working now
So I overwrite the FlatPage model in my portfolio app as described:
from django.contrib.flatpages.models import FlatPage
from project import Project
from django.db import models
from django.contrib.sites.models import Site
class ProjectFlatPage(FlatPage):
prefix = models.CharField(max_length=100)
project = models.ForeignKey(Project)
which allows me to associate this flatpage with a particular project,
Then I overwrite the save method to write all the extra information when a user saves (needs to be tidied up):
def save(self, force_insert=False, force_update=False):
self.url = u"%s%s/" % (self.project.get_absolute_url(),self.prefix)
self.enable_comments = False
self.registration_required = False
self.template_name = 'project/project_flatpage.html'
super(FlatPage, self).save(force_insert, force_update)
and I scaleback the admin to just allow the important stuff:
class ProjectFlatPageForm(forms.ModelForm):
prefix = forms.RegexField(label=_("Prefix"), max_length=100, regex=r'^[a-z0-9-]+$'),
class Meta:
model = ProjectFlatPage
class ProjectnFlatPageAdmin(admin.ModelAdmin):
form = ProjectFlatPageForm
so now the user can add a flat page inside my app and associate it with a particular project.
In the admin, they just enter a slug for the page and it automatically gets appended through the save() method like: /projects/project-name/flat-page-name/
The remaining problem is on the template end. I can access the normal flatpage information through the given template tags {{ flatpage.title }} and {{ flatpage.content }} put I have no access to the extra fields of the inherited model (i.e. the project field)
Is there anyway around this?
EDIT2
Having thought about it, the easiest way is to write a template tag to find the projectFlatPage associated with the flatpage and get access to it from there. A bit like overwritting the default flatpages template tags
re the 'Edit 2' part of your question... since your custom flatpage model inherits from FlatPage there is an automatic one-to-one field relation created between them by Django.
So in your template, if you know that the flatpage object passed in is one of your customised ones you can access your extra attributes by:
{{ flatpage.projectflatpage.project }}
Of course. Just write your own flatpage-model, for example:
from django.contrib.flatpages.models import FlatPage
class MyFlatPage(FlatPage):
prefix = models.CharField(max_length=100, editable=False)
url = models.CharField(_('URL'), max_length=100, db_index=True, editable=False)
Then add a proper MyFlatPageAdmin to your admin.py file (if you want to, you can import the flatpageadmin from django.contrib.flatpages.admin and inherit from it). After all, you're using the flatpage-model, but overwrite the urls-field. Now add a signal, which concats the prefix and a automatically generated url suffix to the url (like the object id). You can now add the custom flatpage like:
flatpage = MyFlatPage(prefix='/portfolio/my-super-project/')
Everything clear now?
edit 1
As you can read in the documentation, every flatpage should be a projectflatpage and vice versa.