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.
Related
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
Customer User models was great but if you want to build a project combining multiple apps, each app can assume it's going to have the user model as part of it's models. My plan is to revert to the standard django User and have a profile in each app.
eg.
app1.models.py
from django.contrib.auth.models import User
Class App1User(models.Model):
user = models.OneToOne(User)
special = models.CharField( some field that relates to the user in this app)
app2.models.py
from django.contrib.auth.models import User
Class App2User(models.Model):
user = models.OneToOne(User)
another = models.CharField( some field that relates to the user in this app)
etc.
Then add code to go and get the relevant profile data when needed, maybe adding some middleware or template context processors. Before I go down this route I wanted to check this is the most Djangoeque solution?
Imho yes, you doing it in the right way.
And you don't need any middleware or context processors to get relevant data for the user. OneToOne field is easily accessible as the attribute of the user instance:
{{ user.app1user.special }}
{{ user.app2user.another }}
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.
I've installed a django reusable app (Django-Userena) and would like to overwrite the given models.py file.
I have created an app named 'accounts' that calls from Django-Userena. In my 'accounts' app, I have this models.py file that has a class MyProfile that inherits from Django-Userena class UserenaBaseProfile - class MyProfile(UserenaBaseProfile)
In the UserenaBaseProfile class, there is the following code:
privacy = models.CharField(_('privacy'),
max_length=15,
choices=PRIVACY_CHOICES,
default=userena_settings.USERENA_DEFAULT_PRIVACY,
help_text = _('Designates who can view your profile.'))
I would like to extend privacy with an extra value with 'editable=False,' as I do not want this field to be displayed in the auto-generated form.
I tried several ways like calling privacy again in the MyProfile inherited model with the new settings but I am only made aware of Django's "Field name "hiding" is not permitted" (https://docs.djangoproject.com/en/1.4/topics/db/models/#field-name-hiding-is-not-permitted)
My current solution is to simply include the whole UserenaBaseProfile class in my 'accounts' app models.py before calling class MyProfile(UserenaBaseProfile) below.
This does not look like an elegant solution to me. How do you guys go about overriding the models.py file in the reusable app?
Thank you very much.
In my opinion it could be done in two ways:
Make a fork of Django-Userena with your modified model and you use yours.
Make a wrapper of Django-Userena with your models.py and use your wrapper app.
For the urls.py/views.py you could just put:
#Your wrapper views:
from django-userena.views import *
#your wrapper urls:
from django-userena.urls import *
Here are your models:
#your MODIFIED model:
from django-userena.models import *
# then put you new UserenaBaseProfile
class UserenaBaseProfile(models.Model):
#copy the model fields
...
privacy = models.CharField(_('privacy'),
max_length=15,
choices=PRIVACY_CHOICES,
default=userena_settings.USERENA_DEFAULT_PRIVACY,
help_text = _('Designates who can view your profile.'))
Then you could use your custom app in your project.
If you want to customise templates, create a templates directory in your project and put there your modified template files keeping their original names, so the django template-loader could find yours first (it depends how template-loaders have been configured in your settings.py)