19

I would like to add a field to the Django FlatPage database model, but I do not really know how to extend this without editing the original application.

What I want to do is to add the following field to the model:


from django.db import models
from django.contrib.flatpages.models import FlatPage as FlatPageOld

class FlatPage(FlatPageOld):
    order = models.PositiveIntegerField(unique=True)

How do I get to add this to the FlatPage model?

Thanks in advance

3 Answers3

25

Your approach is fine - you just don't see the result because the old flatpage model is registered in the admin and the new one isn't. Here's what you might do in your new app's admin.py (using less ambiguous naming than what you've got above):

from django.contrib import admin
from django.contrib.flatpages.admin import FlatPageAdmin
from django.contrib.flatpages.forms import FlatpageForm
from django.contrib.flatpages.models import FlatPage

from models import ExtendedFlatPage

class ExtendedFlatPageForm(FlatpageForm):
    class Meta:
        model = ExtendedFlatPage

class ExtendedFlatPageAdmin(FlatPageAdmin):
    form = ExtendedFlatPageForm
    fieldsets = (
        (None, {'fields': ('url', 'title', 'content', 'sites', 'order')}),
    )     

admin.site.unregister(FlatPage)
admin.site.register(ExtendedFlatPage, ExtendedFlatPageAdmin)

Obviously there are a few things going on here, but most importantly the FlatPage model is being unregistered and the ExtendedFlatPage model is being registered in its place.

Gatothgaj
  • 1,633
  • 2
  • 16
  • 27
ozan
  • 9,285
  • 4
  • 30
  • 27
  • 12
    Keep in mind that this approach won't work with the default FlatpageFallbackMiddleware - it will return instances of the original Flatpage model, not your extension. So you'll have to write your own version of it, or use your own URL/view. Also, you've now got two tables where really only one is needed, which results in less-efficient queries. All in all, I'd recommend writing your own flatpage app from scratch, or using the class_prepared approach to monkeypatch the field in, rather than using inheritance. – Carl Meyer Jun 22 '09 at 15:52
7

And the method in your post doesn't work because... ?

If for some reason you really need to fiddle with the builtin FlatPage class and edit it dynamically, you can hook to the class_prepared signal:

http://docs.djangoproject.com/en/dev/ref/signals/#class-prepared

Edit

Here's how you'd do it with a class_prepared:

from django.db.models.signals import class_prepared
from django.db import models

def alter_flatpages(sender, **kwargs):
    if sender.__module__ == 'django.contrib.flatpages.models' and sender.__name__ == 'FlatPage':
        order = models.IntegerField()
        order.contribute_to_class(sender, 'order')

class_prepared.connect(alter_flatpages)

Put this in, say, 'signals.py' in the same directory as your settings.py, and add 'signals' to the top (this is important, to make sure the signal handler gets installed in time) of the INSTALLED_APPS list .

However, this still won't get the field displayed in Admin, because there's a custom ModelAdmin class for FlatPages which explicitely lists the fields. So after it gets registered in the flatpages app, you'd need to unregister it somewhere (admin.site.unregister) and register a ModelAdmin of your own.

oggy
  • 1,855
  • 1
  • 13
  • 10
  • The field isnt showing up in the admin? I dont understand why. That's the problem –  Jun 20 '09 at 13:07
  • Well there's no reason it should show in the admin for the builtin Django class - you haven't modified it! If you create your own FlatPage, in a separate app, you need to register your new class with the admin. If you don't want to do that, you can go the class_prepared way, but this assumes some pretty good knowledge of Python. – oggy Jun 20 '09 at 14:06
  • 1
    Hmm ok, I thought I would be able to monkeypatch the original FlatPage class so it actually would show up in the admin without creating a new app (and models). Can you give an example of the class_prepared way? –  Jun 20 '09 at 17:04
  • I have added an answer that should solve this problem – Yannic Hamann May 11 '20 at 06:08
0
  1. In your settings.py change the location of the migrations folder for the django.contrib.flatpages app to another folder than the default one. For example:

settings.py:

MIGRATION_MODULES = {'flatpages': 'yourproject.flatpages.migrations'}

Keep in mind that you have to create empty __init__.py files within the folders yourproject, flatpages and migrations to make Python treat those dictionaries as packages.

  1. Execute the makemigrations management command to populate the initial migration to your MIGRATION_MODULES folder. It should look similar to the default one.

  2. Within the apps.py of one of your apps (preferably the one using the flatpages feature) add oggy's class_prepared solution:

apps.py:

from django.apps import AppConfig
from django.db.models.signals import class_prepared
from django.db import models


def alter_flatpages(sender, **kwargs):
    if sender.__module__ == 'django.contrib.flatpages.models' and sender.__name__ == 'FlatPage':
        meta_description = models.CharField(max_length=255, blank=True, null=True)
        meta_description.contribute_to_class(sender, 'meta_description')


class TexteConfig(AppConfig):
    name = 'marlene.texte'

    def __init__(self, app_name, app_module):
        super().__init__(app_name, app_module)
        class_prepared.connect(alter_flatpages)
  1. Add another migration by executing makemigrations again. This time you have added the CharField meta_description to the model. migrate to apply the changes to the database.

  2. Modify the FlatPageAdmin:

admin.py:

from django.contrib.flatpages.admin import FlatPageAdmin
from django.contrib.flatpages.models import FlatPage

class MarleneFlatPageAdmin(FlatPageAdmin):
    fieldsets = (
        (None, {'fields': ('url', 'title', 'content', 'meta_description', 'sites')}),
        (_('Advanced options'), {
            'classes': ('collapse',),
            'fields': ('registration_required', 'template_name'),
        }),
    )


admin.site.unregister(FlatPage)
admin.site.register(FlatPage, MarleneFlatPageAdmin)

The biggest drawback of this solution is that if Django adds new migrations to the flatpage app in the future you are responsible for managing them. I would advise everyone not to use the flatpage app no matter if seems a good fit for your current situation.

Yannic Hamann
  • 4,655
  • 32
  • 50