Do something once in django - django

I want to create two groups once per project's life. So, I read about AppConfig
And I created core.appconfig.py:
from django.apps import AppConfig
from django.contrib.auth.models import Group
class RolesConfig(AppConfig):
name = 'roles_config'
verbose_name = 'Roles configuration'
def create_roles(self):
driver = Group.objects.create(name='driver')
manager = Group.objects.create(name='manager')
driver.save()
manager.save()
And the in settings.py: default_app_config = 'core.appconfig.RolesConfig'
But when I run server and go to the admin page, there are no groups. Why? When is AppConfig called?

Consider using a data migration:
Create an empty migration file with manage.py makemigrations <app_name> --empty.
Create a function that adds the default values to the database.
def create_roles(apps, schema_editor):
Group = apps.get_model('auth', 'Group')
driver = Group.objects.create(name='driver')
manager = Group.objects.create(name='manager')
Add a RunPython operation to you migrations:
class Migration(migrations.Migration):
operations = [
migrations.RunPython(create_roles),
]
Automatic loading of data fixtures has been deprecated in favour of data migrations.

I consider #Leistungsabfall answer to be correct, apart from that: don't do this. App config was not built for this purpose, instead you should create fixtures: https://docs.djangoproject.com/en/1.8/howto/initial-data/ .
App config is run every time you run the application so it would not really work.

There are several things wrong here:
Make sure the path to appconfig.py is myapp/appconfig.py.
Make sure that name is your Django application name (e.g. myapp).
Rename create_roles(self) to ready(self).
In myapp/__init__.py (create this file if it doesn't exist) add this line:
default_app_config = 'myapp.appconfig.RolesConfig'
Remove driver.save() and manager.save(), they are redundant because create() already does save the objects in the database.
(Replace myapp with your Django application name.)

Related

how to generate builtin model permissions for non-managed models?

I have model like this:
class Venue(models.Model):
name = models.CharField(max_length=255)
class Meta:
managed = False
db_table = 'venue'
permissions = [
('change_venue', 'Can change venue'),
]
It is not managed because it already exists in the database (which was created before django project).
I want to use django's builtin model permissions, but they are not created by default. I tried to add them by changing Meta.permissions field but got an error: The permission codenamed 'change_venue' clashes with a builtin permission for model 'events.Venue'
What should I do? Just make migration and create permissions manually?
Fixed by creating permissions in App.ready hook:
from django.apps import AppConfig
from django.contrib.auth.management import create_permissions
class MyAppConfig(AppConfig):
name = 'myapp'
def ready(self):
create_permissions(self)
Don't know if this counts as valid solution tho
Edit 1
Method above didn't work for new database because models "were not ready/create" when app is ready. So I switched to post_migrate signal, and everything was fine.
Edit 2
After some time I have found global problem about why I don't have permissions and content types in the first place: I simply didn't make migrations for un-managed models. With migrations everything is fine.

How to define default data for Django Models?

I want my application to have default data such as user types.
What's the most efficient way to manage default data after migrations?
It needs to handle situations such as, after I add a new table, it adds the default data for it.
You need to create an empty migration file and Do your stuff in operations block, as explained in docs.
Data Migrations
As well as changing the database schema, you can also use migrations to change the data in the database itself, in conjunction with the schema if you want.
Now, all you need to do is create a new function and have RunPython use it
Docs explains this with an example to show ,how to communicate with your models.
From Docs
To create an empty migration file,
python manage.py makemigrations --empty yourappname
And this is the example how to update a newly added field.
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
def combine_names(apps, schema_editor):
# We can't import the Person model directly as it may be a newer
# version than this migration expects. We use the historical version.
Person = apps.get_model("yourappname", "Person")
for person in Person.objects.all():
person.name = "%s %s" % (person.first_name, person.last_name)
person.save()
class Migration(migrations.Migration):
initial = True
dependencies = [
('yourappname', '0001_initial'),
]
operations = [
migrations.RunPython(combine_names),
]
The accepted answer is fine. But, since OP asked the question in the context of adding new rows and not updating existing entries. Here is the code snippet for adding new entries :
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('loginmodule', '0002_login_avatar'),
]
def insertData(apps, schema_editor):
Login = apps.get_model('loginmodule', 'Login')
user = Login(name = "admin", login_id = "admin", password = "password", email = "admin#pychat.com", type = "Admin", avatar="admin.jpg")
user.save()
operations = [
migrations.RunPython(insertData),
]
Update:
most users are looking for data migration as suggested by #durdenk in https://stackoverflow.com/a/39742847/3627387. But what OP was asking is about a way to add data after migrations, that is why this is accepted answer.
Original answer:
I think what you are looking for is fixtures https://docs.djangoproject.com/en/1.10/howto/initial-data/
From docs
It’s sometimes useful to pre-populate your database with hard-coded data when you’re first setting up an app. You can provide initial data via fixtures.
Also read this https://code.djangoproject.com/wiki/Fixtures
Answer is given above just to show how to insert new rows to the table.
from django.db import migrations, models
from yourapp.models import <yourmodel>
def combine_names(apps, schema_editor):
obj = <yourmodel>(arrib=value)
obj.save()
For example let's say you have model Person
person = Person(first_name='raj', last_name='shah')
person.save()

Create django groups programmatically

I want to create groups in django programmatically, but not in a view, but rather in something like model (for example using migrations). How to do it? There's no information about it in google and docs (at least not here: https://docs.djangoproject.com/en/1.7/topics/auth/default/#groups)
Okay, it seems you're using Django 1.7's new migrations system. This is similar to but not exactly like South.
A migration that involves altering the data in the tables is a data migration, and you typically need to write Python code to do the migration.
From the Django docs, there's this example:
# -*- coding: utf-8 -*-
from django.db import models, migrations
def combine_names(apps, schema_editor):
# We can't import the Person model directly as it may be a newer
# version than this migration expects. We use the historical version.
Person = apps.get_model("yourappname", "Person")
for person in Person.objects.all():
person.name = "%s %s" % (person.first_name, person.last_name)
person.save()
class Migration(migrations.Migration):
dependencies = [
('yourappname', '0001_initial'),
]
operations = [
migrations.RunPython(combine_names),
]
Note that the code to run during the migration is in the combine_names function, which is called by the migrations.RunPython(combine_names) entry in the operations list of the migration. Your migration should do its group creation in a function like that, along with whatever other data migration is needed.
You should probably use a line like
Group = apps.get_model("auth", "Group")
my_group, created = Group.objects.get_or_create(name='group1')
to create your groups, in case there is already a group of that name in the table.
Don't put code to run during a migration into the root level of the Python file; if you do so, it will be run every time that migration is imported, for example, every time you run ./manage.py runserver.
P.S. You need to put your migrations.RunPython entry at the right point in the operations list; it won't work if you put it after an operation that deletes a table it needs, for example.
Groups are just like any other Django model. You can create them as you would anything else.
my_group = Group.objects.create(name='group1')

Django Proxy Model Permissions Do Not Appear

I extended Django admin site for my app to allow non-staff/superusers access. This is working just fine.
I created a proxy model for an existing model and registered it to my admin site, however, it doesn't appear for non-staff users. From the documentation I read, my understanding is that proxy models get their own permissions. I checked and these don't appear in the list of available permissions.
Here's my code in case it helps:
Normal Model
class Engagement(models.Model):
eng_type = models.CharField(max_length=5)
environment = models.CharField(max_length=8)
is_scoped = models.BooleanField()
class Meta:
ordering = ['eng_type', 'environment']
app_label = 'myapp'
Proxy Model
class NewRequests(Engagement):
class Meta:
proxy = True
app_label = 'myapp'
verbose_name = 'New Request'
verbose_name_plural = 'New Requests'
Model Admin
class NewRequestsAdmin(ModelAdmin):
pass
def queryset(self, request):
return self.model.objects.filter(is_scoped=0)
Custom Admin Registration
myapps_admin_site.register(NewRequests, NewRequestsAdmin)
I've been managing my DB with South. According to this post, you have to tamper with it a bit by following the instructions it points users to. This was a failure. My DB doesn't have a whole lot of info in it, so I uncommented South and ran a regular syncdb to rule out South. Unfortunately, this is still not working and I'm at a loss. Any help is appreciated.
Edit
This was on Django 1.4
Turns out I didn't do anything wrong. I was looking for the permissions under
myapp | New Request | Can add new request
Permissions fall under the parent model.
myapp | engagement | Can add new request
This is fixed in Django 2.2, quoting release notes:
Permissions for proxy models are now created using the content type of the proxy model rather than the content type of the concrete model. A migration will update existing permissions when you run migrate.
and docs:
Proxy models work exactly the same way as concrete models. Permissions are created using the own content type of the proxy model. Proxy models don’t inherit the permissions of the concrete model they subclass.
There is a workaround, you can see it here: https://gist.github.com/magopian/7543724
It can vary based on your django version, but the priciple is the same.
Tested with Django 1.10.1
# -*- coding: utf-8 -*-
"""Add permissions for proxy model.
This is needed because of the bug https://code.djangoproject.com/ticket/11154
in Django (as of 1.6, it's not fixed).
When a permission is created for a proxy model, it actually creates if for it's
base model app_label (eg: for "article" instead of "about", for the About proxy
model).
What we need, however, is that the permission be created for the proxy model
itself, in order to have the proper entries displayed in the admin.
"""
from __future__ import unicode_literals, absolute_import, division
import sys
from django.contrib.auth.management import _get_all_permissions
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.core.management.base import BaseCommand
from django.apps import apps
from django.utils.encoding import smart_text
class Command(BaseCommand):
help = "Fix permissions for proxy models."
def handle(self, *args, **options):
for model in apps.get_models():
opts = model._meta
ctype, created = ContentType.objects.get_or_create(
app_label=opts.app_label,
model=opts.object_name.lower(),
defaults={'name': smart_text(opts.verbose_name_raw)})
for codename, name in _get_all_permissions(opts):
p, created = Permission.objects.get_or_create(
codename=codename,
content_type=ctype,
defaults={'name': name})
if created:
sys.stdout.write('Adding permission {}\n'.format(p))
How to use
create a directory /myproject/myapp/management/commands
create the file /myproject/myapp/management/__init__.py
create the file /myproject/myapp/management/commands/__init__.py
save the code above into /myproject/myapp/management/commands/fix_permissions.py
run /manage.py fix_permissions
This is a known bug in Django: https://code.djangoproject.com/ticket/11154 (check comments for some patches)
As of 2021 and Django 3+, the solution for missing permissions for proxy model is simple, just generate migrations with makemigrations:
app#e31a3ffef22c:~/app$ python manage.py makemigrations my_app
Migrations for 'main':
main/migrations/0193_myproxymodel.py
- Create proxy model MyProxyModel
I came here and wasn't really sure, what is the correct cause/solution to this problem.
For Django 1.11
This issue is related due to the wrong content_type_id in auth_permission table.
By default, it adds the content type of the base model instead of proxy model content type.
I realize this question was closed a while ago, but I'm sharing what worked for me in case it might help others.
It turns out that even though permissions for the proxy models I created were listed under the parent apps (as #chirinosky) has mentioned, and even though I granted my non-super user all permissions, it was still denied access to my proxy models through the admin.
What I had to do was workaround a known Django bug (https://code.djangoproject.com/ticket/11154) and connect to the post_syncdb signal to properly create permissions for the proxy models. The code below is modified from https://djangosnippets.org/snippets/2677/ per some of the comments on that thread.
I placed this in myapp/models.py that held my proxy models. Theoretically this can live in any of your INSTALLED_APPS after django.contrib.contenttypes because it needs to be loaded after the update_contenttypes handler is registered for the post_syncdb signal so we can disconnect it.
def create_proxy_permissions(app, created_models, verbosity, **kwargs):
"""
Creates permissions for proxy models which are not created automatically
by 'django.contrib.auth.management.create_permissions'.
See https://code.djangoproject.com/ticket/11154
Source: https://djangosnippets.org/snippets/2677/
Since we can't rely on 'get_for_model' we must fallback to
'get_by_natural_key'. However, this method doesn't automatically create
missing 'ContentType' so we must ensure all the models' 'ContentType's are
created before running this method. We do so by un-registering the
'update_contenttypes' 'post_syncdb' signal and calling it in here just
before doing everything.
"""
update_contenttypes(app, created_models, verbosity, **kwargs)
app_models = models.get_models(app)
# The permissions we're looking for as (content_type, (codename, name))
searched_perms = list()
# The codenames and ctypes that should exist.
ctypes = set()
for model in app_models:
opts = model._meta
if opts.proxy:
# Can't use 'get_for_model' here since it doesn't return
# the correct 'ContentType' for proxy models.
# See https://code.djangoproject.com/ticket/17648
app_label, model = opts.app_label, opts.object_name.lower()
ctype = ContentType.objects.get_by_natural_key(app_label, model)
ctypes.add(ctype)
for perm in _get_all_permissions(opts, ctype):
searched_perms.append((ctype, perm))
# Find all the Permissions that have a content_type for a model we're
# looking for. We don't need to check for codenames since we already have
# a list of the ones we're going to create.
all_perms = set(Permission.objects.filter(
content_type__in=ctypes,
).values_list(
"content_type", "codename"
))
objs = [
Permission(codename=codename, name=name, content_type=ctype)
for ctype, (codename, name) in searched_perms
if (ctype.pk, codename) not in all_perms
]
Permission.objects.bulk_create(objs)
if verbosity >= 2:
for obj in objs:
sys.stdout.write("Adding permission '%s'" % obj)
models.signals.post_syncdb.connect(create_proxy_permissions)
# See 'create_proxy_permissions' docstring to understand why we un-register
# this signal handler.
models.signals.post_syncdb.disconnect(update_contenttypes)

Django Models not Showing Up in DB after syncdb

I have a models folder that has a few models in files that are already in the DB. I have just added another file/model but it is not being added to the DB when I run syncdb. I've tried manage.py validate and it is running fine. I have also run the code and it only fails when it tries to save with "table does not exist".
the original structure was like this:
/models
-- __init__.py
-- file1.py
-- file2.py
and __init__.py looked like:
from file1 import File1Model
from file2 import File2Model
I added file3.py
/models
-- __init__.py
-- file1.py
-- file2.py
-- file3.py
and modified __init__.py
from file1 import File1Model
from file2 import File2Model
from file3 import File3Model
And the contents of file3 (names may have been changed to protect the innocent, but besides that its the exact file):
UPDATE: just tried adding a primary key since the id field may have been messing with the automatically added integer primary key id. Also tried a few variations but no dice.
from django.db import models
from django.contrib.auth.models import User
class File3Model(models.Model):
user = models.OneToOneField(User)
token = models.CharField(max_length=255, blank=False, null=False)
id = models.CharField(primary_key=True, max_length=255)
class Admin:
pass
class Meta:
app_label = 'coolabel'
def __unicode__(self):
return self.user.username
#staticmethod
def getinstance(user, token, id):
try:
instance = File3Model.objects.get(pk=id)
if instance.token != token:
instance.token = token
instance.save()
return instance
except:
pass
instance = File3Model()
instance.user = user
instance.token = token
instance.id = id
instance.save()
return instance
So in this example, File1Model and File2Model are already in the DB and remain in the DB after syncdb. However, File3Model is not added even after rerunning syncdb. Is there any way to figure out why the new model isn't being added??
If you define the model outside of models.py, you have to set the app_label attribute on the models Meta class.
Edit: The app_label has to refer to an app in your INSTALLED_APPS setting. It should probably match the name of the app that the models directory is in, unless you've got a really good reason to do otherwise. That seems to have been your problem here.
class File3Model(models.Model):
foo = models.CharField(...)
...
class Meta:
app_label = "my_app"
Note that syncdb will never remove any tables from the db. The other tables were probably created with syncdb before the models.py was replaced with the directory structure.
set app_label to my app solves my problem.
Why did you split you models and having models folder instead of placing models in models.py ?
In my own project there are about 10 models live in models.py and I'm fine with it.
You can also try manage.py syncdb --all.
And I think its better to keep all models in one single file and import them like from my_app.models import model_name instead of keeping in mind to import necessary model into models/__init__.py. By the way you avoid many problems and long lined imports and don't care about where some_model lives among models/*.py files.
Thanks,
Sultan
BOOM!
I was using a different app_label for the new model but it has to be the same across the model group.
The other models labels were "mediocrelabel" and my new model had the label "coolabel". I changed the new model's label to "mediocrelabel" and now they are being added to the DB correctly.
Thanks for your help, folks!