i would like to export to a XLS file my data in the Admin, i tried Import-Export (with admin integration) and works great, the problem here is, i'm already using a model, so i need to export a proxy model to XLS, can you help me please?, with this code the model appears, and the import, export menu appears too, but when i try to export or import i got this error:
**StopIteration
No exception message supplied**
Admin.py:
from django.contrib import admin
from models import *
from django.utils.html import format_html_join
from import_export import resources
from import_export.admin import ImportExportModelAdmin
class ExportarDatosPromo(Promo):
class Meta:
proxy = True
class PromoCountResource(resources.ModelResource):
class Meta:
model = Promo
class PromoCountAdmin(ImportExportModelAdmin):
fields = ('name', 'title_description1', 'description1', 'offer', 'coupon_code', 'count')
readonly_fields = ('name', 'title_description1', 'description1', 'offer', 'coupon_code', 'count')
admin.site.register(ExportarDatosPromo, PromoCountAdmin)
Well, because nobody answer anything, i fix my error. The problem is, if you have a Taggit (Django-Taggit) field in your model, Django Import-Export App will not work, so you have to delete the field (and there is no other app for tagging actually working), and import-export will work just fine.
If you want to continue to use django taggit as an app, just put your field in a model with a foreign key to your original model.
Related
I'm working on a simple DRF API and I can't seem to access one of my many models from the admin site, although I can access some models from the same Application, please help!
project/app/models.py looks something like this:
class ImportantModel(models.Model):
name = # character field
relation1 = # foreign key
relation2 = # foreign key
...
class Contact(models.Model):
information = # character field
...
The problem is I can see the Contact model(and multiple other models) from the admin site(http://localhost:8000/admin/) but ImportantModel is not showing. Thank you in advance.
You need to register your model in admin.py file.
Well I was in same problem because the problem is I registered my models in admin.py file but in a wrong way
wrong way:
from django.contrib import admin
from .models import Article
from .models import User
# Register your models here.
admin.register(Article)
admin.register(User)
And the right way is
right way:
from django.contrib import admin
from .models import Article
from .models import User
# Register your models here.
admin.site.register(Article)
admin.site.register(User)
Any suggestion on this please?
Initially I had class Customer(): in models.py
This is how the code in admin.py looked
from django.contrib import admin
from booknowapp.models import Customer
# Register your models here.
admin.site.register(Customer)
Now that I have added two new classes to models how do I register in admin for other new two classes to appear in the app? I am not sure of the syntax to be used.
If your added two new classes of models are ModelClass1 and ModelClass2 then you can register multiple models in admin.py like :
from django.contrib import admin
from booknowapp.models import Customer, ModelClass1, ModelClass2
myModels = [Customer, ModelClass1, ModelClass2] # iterable list
admin.site.register(myModels)
OR
You can repeat admin.site.register for other two new classes just like your Customer .
If you extend the syntax that you have already used, it would simply be:
from django.contrib import admin
# wrap the line if it's too long
from booknowapp.models import (
Customer,
SecondModel,
ThirdModel
)
# Register your models here.
admin.site.register(Customer)
admin.site.register(SecondModel)
admin.site.register(ThirdModel)
However, this will only give you the default admin model list views - which you will probably want to extend.
class CustomerAdmin(admin.ModelAdmin):
"""Specialised admin view for the Customer model."""
# set the fields to display
list_display = ('name', 'address', 'registered')
# register your Customer model, using the CustomerAdmin view
admin.site.register(Customer, CustomerAdmin)
The ModelAdmin has lots more functionality that you can leverage - search fields, filtering, custom fields, custom actions ('Activate customer'), which you can read about here - http://www.djangobook.com/en/2.0/chapter06.html#custom-modeladmin-classes
I'm trying to extend default Django's model with a new field.
In localsite/models.py I have the following code:
from django.db import models
from django.utils.translation import ugettext_lazy as _
from satchmo_store.contact.models import Organization
class OrganizationExtra(models.Model):
organization = models.OneToOneField(Organization,
verbose_name=_('Organization'), primary_key=True )
vat_number = models.CharField(_('VAT'), max_length=12)
Followed with run of ./manage.py syncdb which did created a new table for above model. So far so good.
Now I'm trying to add this new field in related Organization view in the admin interface.
The following code registers the new menu, however the new vat_number field is not displayed in view of the related Organization model.
from django.contrib import admin
from localsite.models import ProductResource, OrganizationExtra
admin.site.register(OrganizationExtra)
The original Organization model is registered with
from satchmo_store.contact.models import Organization
from django.contrib import admin
class OrganizationOptions(admin.ModelAdmin):
list_filter = ['type', 'role']
list_display = ['name', 'type', 'role']
admin.site.register(Organization, OrganizationOptions)
Any idea how to insert my new field without touching original Satchmo sources ?
See the docs as usual.
One possible way is to create new MyOrganization derived from Organization and register it in place of satchmo one
Your models.py
from django.db import models
from django.utils.translation import ugettext_lazy as _
from satchmo_store.contact.models import Organization
class MyOrganization(Organization):
vat_number = models.CharField(_('VAT'), max_length=12)
Your admin.py
from django.contrib import admin
from localsite.models import MyOrganization
from satchmo_store.contact.models import Organization
from satchmo_store.contact.admin import OrganizationOptions
admin.site.unregister(Organization)
admin.site.register(MyOrganization, OrganizationOptions)
Another possible solution (if you wish to stick with OrganizationExtra) is to create custom form for Organization for admin interface and again reregister model. By it seems to me as more boilerplate and result will be the same.
NB: in both cases DB structure would be the same, i.e. extra table would be created.
in django by default when syncdb is run with django.contrib.auth installed, it creates default permissions on each model... like foo.can_change , foo.can_delete and foo.can_add. To add custom permissions to models one can add class Meta: under the model and define permissions there, as explained here https://docs.djangoproject.com/en/4.1/topics/auth/customizing/#custom-permissions
My question is that what should I do if I want to add a custom permission to the User model? like foo.can_view. I could do this with the following snippet,
ct = ContentType.objects.get(app_label='auth', model='user')
perm = Permission.objects.create(codename='can_view', name='Can View Users',
content_type=ct)
perm.save()
But I want something that plays nicely with syncdb, for example the class Meta under my custom models. Should I just have these in class Meta: under UserProfile since that is the way to extend the user model. but is that the RIGHT way to do it? Wouldn't that tie it to UserProfile model?
You could do something like this:
in the __init__.py of your Django app add:
from django.db.models.signals import post_syncdb
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth import models as auth_models
from django.contrib.auth.models import Permission
# custom user related permissions
def add_user_permissions(sender, **kwargs):
ct = ContentType.objects.get(app_label='auth', model='user')
perm, created = Permission.objects.get_or_create(codename='can_view', name='Can View Users', content_type=ct)
post_syncdb.connect(add_user_permissions, sender=auth_models)
I don't think there is a "right" answer here, but i used the exact same code as you except i changed Permission.objects.create to Permission.objects.get_or_create and that worked find to sync with syncdb
An updated answer for Django 1.8. The signal pre_migrate is used instead of pre_syncdb, since syncdb is deprecated and the docs recommend using pre_migrate instead of post_migrate if the signal will alter the database. Also, #receiver is used to connect add_user_permissions to the signal.
from django.db.models.signals import pre_migrate
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth import models as auth_models
from django.contrib.auth.models import Permission
from django.conf import settings
from django.dispatch import receiver
# custom user related permissions
#receiver(pre_migrate, sender=auth_models)
def add_user_permissions(sender, **kwargs):
content_type = ContentType.objects.get_for_model(settings.AUTH_USER_MODEL)
Permission.objects.get_or_create(codename='view_user', name='View user', content_type=content_type)
This is a bit hacky but mentioning it here anyway for reference.
My site has a generic model called Setting, which stores various settings concerning the site I want certain users to be able to edit, without needing to go through me the developer (like registration limit, or an address, or the cost of items, etc).
All the permissions that don't nicely map onto other models (eg "Send Password Reminder Email to Student", "Generate Payment Reconciliation Report", "Generate PDF Receipt"), which really just relate to pages that get viewed in the admin area, get dumped onto this Setting model.
For example, here's the model:
class Setting(models.Model):
name = models.CharField(max_length=50, unique=True)
slug = models.SlugField(editable=False)
description = models.TextField()
value = models.TextField()
class Meta:
#for permissions that don't really relate to a particular model, and I don't want to programmatically create them.
permissions = (
("password_reminder", "Send Password Reminder"),
("generate_payment_reconciliation_report", "Generate Payment Reconciliation Report"),
("generate_pdf_receipt", "Generate PDF Receipt"),
)
Do each of those settings strictly relate to the Setting model? No, which is why I said this is a bit hacky. But it is nice that I can now just dump all those permissions here, and Django's migration commands will take care of the rest.
What is the best approach to extending the Site model in django? Creating a new model and ForeignKey the Site or there another approach that allows me to subclass the Site model?
I prefer subclassing, because relationally I'm more comfortable, but I'm concerned for the impact it will have with the built-in Admin.
I just used my own subclass of Site and created a custom admin for it.
Basically, when you subclass a model in django it creates FK pointing to parent model and allows to access parent model's fields transparently- the same way you'd access parent class attributes in pyhon.
Built in admin won't suffer in any way, but you'll have to un-register Sites ModelAdmin and register your own ModelAdmin.
If you only want to change behaviour of the object, but not add any new fields, you should consider using a "proxy model" (new in Django 1.1). You can add extra Python methods to existing models, and more:
This is what proxy model inheritance is for: creating a proxy for the original model. You can create, delete and update instances of the proxy model and all the data will be saved as if you were using the original (non-proxied) model. The difference is that you can change things like the default model ordering or the default manager in the proxy, without having to alter the original.
Read more in the documentation.
As of Django 2.2 there still no simple straight way to extend Site as can be done for User. Best way to do it now is to create new entity and put parameters there. This is the only way if you want to leverage existing sites support.
class SiteProfile(models.Model):
title = models.TextField()
site = models.OneToOneField(Site, on_delete=models.CASCADE)
You will have to create admin for SiteProfile. Then add some SiteProfile records with linked Site. Now you can use site.siteprofile.title anywhere where you have access to current site from model.
You can have another model like SiteProfile which has a OneToOne relation with Site.
It has been a long time since the question was asked, but I think there is not yet (Django 3.1) an easy solution for it like creating a custom user model. In this case, creating a custom user model inheriting from django.contrib.auth.models.AbstractUser model and changing AUTH_USER_MODEL (in settings) to the newly created custom user model solves the issue.
However, it can be achieved for also Site model with a long solution written below:
SOLUTION
Suppose that you have an app with the name core. Use that app for all of the code below, except the settings file.
Create a SiteProfile model with a site field having an OneToOne relation with the Site model. I have also changed its app_label meta so it will be seen under the Sites app in the admin.
# in core.models
...
from django.contrib.sites.models import Site
from django.db import models
class SiteProfile(models.Model):
"""SiteProfile model is OneToOne related to Site model."""
site = models.OneToOneField(
Site, on_delete=models.CASCADE, primary_key=True,
related_name='profiles', verbose_name='site')
long_name = models.CharField(
max_length=255, blank=True, null=True)
meta_name = models.CharField(
max_length=255, blank=True, null=True)
def __str__(self):
return self.site.name
class Meta:
app_label = 'sites' # make it under sites app (in admin)
...
Register the model in the admin. (in core.admin)
What we did until now was good enough if you just want to create a site profile model. However, you will want the first profile to be created just after migration. Because the first site is created, but not the first profile related to it. If you don't want to create it by hand, you need the 3rd step.
Write below code in core.apps.py:
# in core.apps
...
from django.conf import settings
from django.db.models.signals import post_migrate
def create_default_site_profile(sender, **kwargs):
"""after migrations"""
from django.contrib.sites.models import Site
from core.models import SiteProfile
site = Site.objects.get(id=getattr(settings, 'SITE_ID', 1))
if not SiteProfile.objects.exists():
SiteProfile.objects.create(site=site)
class CoreConfig(AppConfig):
name = 'core'
def ready(self):
post_migrate.connect(create_default_site_profile, sender=self)
from .signals import (create_site_profile) # now create the second signal
The function (create_default_site_profile) will automatically create the first profile related to the first site after migration, using the post_migrate signal. However, you will need another signal (post_save), the last row of the above code.
If you do this step, your SiteProfile model will have a full connection with the Site model. A SiteProfile object is automatically created/updated when any Site object is created/updated. The signal is called from apps.py with the last row.
# in core.signals
from django.contrib.sites.models import Site
from django.db.models.signals import post_save, post_migrate
from django.dispatch import receiver
from .models import SiteProfile
#receiver(post_save, sender=Site)
def create_site_profile(sender, instance, **kwargs):
"""This signal creates/updates a SiteProfile object
after creating/updating a Site object.
"""
siteprofile, created = SiteProfile.objects.update_or_create(
site=instance
)
if not created:
siteprofile.save()
Would you like to use it on templates? e.g.
{{ site.name }}
Then you need the 5th and 6th steps.
Add the below code in settings.py > TEMPLATES > OPTIONS > context_processors
'core.context_processors.site_processor'
# in settings.py
TEMPLATES = [
{
# ...
'OPTIONS': {
'context_processors': [
# ...
# custom processor for getting the current site
'core.context_processors.site_processor',
],
},
},
]
Create a context_processors.py file in the core app with the code below.
A try-catch block is needed (catch part) to make it safer. If you delete all sites from the database you will have an error both in admin and on the front end pages. Error is Site matching query does not exist. So the catch block creates one if it is empty.
This solution may not be fully qualified if you have a second site and it is deleted. This solution only creates a site with id=1.
# in core.context_processors
from django.conf import settings
from django.contrib.sites.models import Site
def site_processor(request):
try:
return {
'site': Site.objects.get_current()
}
except:
Site.objects.create(
id=getattr(settings, 'SITE_ID', 1),
domain='example.com', name='example.com')
You can now use the site name, domain, meta_name, long_name, or any field you added, in your templates.
# e.g.
{{ site.name }}
{{ site.profiles.long_name }}
It normally adds two DB queries, one for File.objects and one for FileProfile.objects. However, as it is mentioned in the docs,
Django is clever enough to cache the current site at the first request and it serves the cached data at the subsequent calls.
https://docs.djangoproject.com/en/3.1/ref/contrib/sites/#caching-the-current-site-object
Apparently, you can also create a models.py file in a folder that you add to INSTALLED_APPS, with the following content:
from django.contrib.sites.models import Site as DjangoSite, SiteManager
from django.core.exceptions import ImproperlyConfigured
from django.db import models
from django.http.request import split_domain_port
# our site model
class Site(DjangoSite):
settings = models.JSONField(blank=True, default={})
port = models.PositiveIntegerField(null=True)
protocol = models.CharField(default='http', max_length=5)
#property
def url(self):
if self.port:
host = f'{self.domain}:{self.port}'
else:
host = self.domain
return f'{self.protocol}://{host}/'
# patch django.contrib.sites.models.Site.objects to use our Site class
DjangoSite.objects.model = Site
# optionnal: override get_current to auto create site instances
old_get_current = SiteManager.get_current
def get_current(self, request=None):
try:
return old_get_current(self, request)
except (ImproperlyConfigured, Site.DoesNotExist):
if not request:
return Site(domain='localhost', name='localhost')
host = request.get_host()
domain, port = split_domain_port(host)
Site.objects.create(
name=domain.capitalize(),
domain=host,
port=port,
protocol=request.META['wsgi.url_scheme'],
)
return old_get_current(self, request)
SiteManager.get_current = get_current
In my opinion, the best way to doing this is by writing a model related to the site model using inheritance
First, add the site id to the Django settings file
SITE_ID = 1
now create a model in one of your apps
from django.db import models
from django.contrib.sites.models import Site
class Settings(Site):
field_a = models.CharField(max_length=150, null=True)
field_b = models.CharField(max_length=150, null=True)
class Meta:
verbose_name_plural = 'settings'
db_table = 'core_settings' # core is name of my app
def __str__(self) -> str:
return 'Settings'
then edit the apps.py file of that app
from django.apps import AppConfig
from django.db.models.signals import post_migrate
def build_settings(sender, **kwargs):
from django.contrib.sites.models import Site
from .models import Settings
if Settings.objects.count() < 1:
Settings.objects.create(site_ptr=Site.objects.first())
class CoreConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'project.apps.core'
def ready(self) -> None:
post_migrate.connect(build_settings, sender=self)
now every time you run migrations a row will be auto-generated in core_settings that have a one to one relationship with your Site model
and now you can access your settings like this
Site.objects.get_current().settings.access_id
optional: if have only one site
unregister site model from admin site and disable deleting and creating settings model in admin site
from django.contrib import admin
from . import models
from django.contrib.sites.models import Site
admin.site.unregister(Site)
#admin.register(models.Settings)
class SettingAdminModel(admin.ModelAdmin):
def has_delete_permission(self, request,obj=None) -> bool:
return False
def has_add_permission(self, request) -> bool:
return False