How to override a Django reusable app's model? - django

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)

Related

How to skip dummy model from django test

I have created one django admin page which is associated with a dummy model which doesn't exists.
Here is the code. The statistic model doesn't exists in database.
class StatisticModel(models.Model):
class Meta:
verbose_name_plural = 'Statistic'
class StatisticModelAdmin(admin.ModelAdmin):
model = StatisticModel
def get_urls(self):
view_name = 'changelist'
return [
path('settings/', my_custom_view, name=view_name),
]
admin.site.register(StatisticModel, StatisticModelAdmin)
In my git workflow I have below command,
python backend/django/manage.py test ./backend/django
Which is failing with error,
django.db.utils.ProgrammingError: relation "monitor_statisticmodel" does not exist
Please advise how can I avoid or skip this error?
https://docs.djangoproject.com/en/3.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_urls
This allows you to create admin pages with custom views without requiring models.
Before my answer: why bother have this model if it's not going to be used? If it's only there for testing, write "abstract = True" in the Meta class. If you don't want to do that then...
Maybe setting abstract=True would work? I'm not sure it will since Django's ModelAdmin might raise an error. What I'd advise is Django doesn't is setting "IS_TESTING" in your settings file. Imo, you should have multiple settings file. Then your model can look like:
class StatisticModel(models.Model)
class Meta:
verbose_name_plural = "Statistic"
abstract = getattr(settings, "IS_TESTING", False)
Another example is via a monkey patch and assuming that model is in admin.py. So during your test case, you can run this before your tests begin:
from project import admin
admin.StatisticModel.Meta.abstract = True

What is the best approach to organize classes in models?

I have one app, is called "perms" - some custom permission system. Inside the model's file I have this class "Role".
# perms models.py
class Role(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL)
perms = models.ManyToManyField(Perm)
class Meta:
abstract = True
#def ... some custom functions
Next, I have some apps in my project e.g. app1, app2, app2 etc. were I am using this Role class. E.g.
# app1 models.py
from perms.models import Role
class App1Role(Role):
# some custom fields
The question is if this is good approach or should I organize everything in a different way: to put all "AppXRole" classes in perms.models and then in appX.models import this particular AppXRole class?
What is the best way to manage this kind of app's dependencies in Django?

How do you create a class using (admin.ModelAdmin) for a polling app

I am working through a Django tutorial at http://lightbird.net/dbe/todo_list.html . I completed Django's official tutorial. When I attempted to sync
from django.db import models
from django.contrib import admin
class Item(models.Model):
name = models.CharField(max_length=60)
created = models.DateTimeField(auto_now_add=True)
priority = models.IntegerField(default=0)
difficult = models.IntegerField(default=0)
class ItemAdmin(admin.ModelAdmin):
list_display = ["name", "priority", "difficult", "created", "done"]
search_fields = ["name"]
admin.site.register(Item, ItemAdmin)
The terminal came back with an error that said admin was not defined. What am I doing wrong? How do I define admin?
Update: I added the whole models file as it looks now
The Django documentation lists multiple steps that need to be done to activate the admin site:
Add 'django.contrib.admin' to your INSTALLED_APPS setting.
The admin has four dependencies - django.contrib.auth, django.contrib.contenttypes, django.contrib.messages and
django.contrib.sessions. If these applications are not in your
INSTALLED_APPS list, add them.
Add django.contrib.messages.context_processors.messages to TEMPLATE_CONTEXT_PROCESSORS and MessageMiddleware to
MIDDLEWARE_CLASSES. (These are both active by default, so you only
need to do this if you’ve manually tweaked the settings.)
Determine which of your application’s models should be editable in the admin interface.
For each of those models, optionally create a ModelAdmin class that encapsulates the customized admin functionality and options for
that particular model.
Instantiate an AdminSite and tell it about each of your models and ModelAdmin classes.
Hook the AdminSite instance into your URLconf. lists a couple of things that you must do the enable Django's admin site.
If you haven't done the first two items, syncdb might complain because it can't find the admin app itself.

Django adding tiny-mce

Please suggest the best way to add tinymce to django admin area. Is it possible to add it by extending /admin/change_form.html in my template directory ?
The best way in my opinion is django-tinymce.
Its awesome and super easy to integrate into your project, plus you can add django-filebrowser in easily for image uploading.
django-tinymce is the way to go. You can use pip to install it. You use it on model fields like so:
from tinymce import models as tinymce_models
class Foo(models.Model):
description = tinymce_models.HTMLField(blank=True, null=True, help_text="You can use HTML markup - be careful!")
If you are using South for DB migrations you need to help it out with this line:
add_introspection_rules([], ["^tinymce.models.HTMLField"])
Works like a charm!
Put the tiny_mce.js library somehwere in your media folder. For example in js/tiny_mce/
Then (for django 1.2) you need to create a custom model admin in your_app/admin.py. Add a class Media with js attribute to it. Example:
from django.contrib import admin
from myapp.models import MyModel
class MyModelAdmin(admin.ModelAdmin):
class Media:
js = ('js/tiny_mce/tiny_mce.js',
'js/admin/textareas.js',)
admin.site.register(MyModel, MyModelAdmin)
In media/js/admin/textareas.js you can add your call to tinyMCE.init. Example:
tinyMCE.init({
mode : "textareas",
theme : "advanced"
});
That's it. Javascript is included automatically. No need to overwrite admin templates.
Note: One thing I forget to mention that in this case it only applies to the admin for MyModel. If you need the same functionality for all yout model's, simply register this custom ModelAdmin to them or add Media classes to exising ModelAdmin classes.

Extend flatpages for a specific application

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.