The goal
Being able to automatically create few groups with predefined permissions (including permissions created automatically by django.contrib.auth.signals).
My Struggle
Apparently there are two ways to do this:
fixtures
signals
The problems with going with the first option are:
You can't possibly know, before hand (except maybe by running the script once and noting the IDs on paper), what are the IDs of the predefined permissions (add_, delete_, change_). -
If you decide to create your own permissions you also don't know how to populate the "pk" field in the fixture, giving that some permissions have already been created.
The problem with going with the second option is:
Apparently your signals are run before any other signals, including django.contrib.auth.signals, therefore you are not able to call its predefined permissions and add them to your groups.
Not only that, but with a simple:
def create_initial_groups(**kwargs):
staff_group = Group(name = 'Staff')
clients_group = Group(name = 'Clients')
staff_group.save()
clients_group.save()
and:
signals.post_syncdb.connect(
create_initial_groups,
dispatch_uid = 'unique_app_identifier.listeners.create_initial_groups'
)
I get an:
django.db.utils.IntegrityError: duplicate key value violates unique
constraint "auth_group_name_key" DETAIL: Key (name)=(Staff) already
exists.
Even with a clean database (running drop schema public cascade; create schema public; on psql before python manage.py syncdb).
Question
So, what's the cleanest way, if any exists, to prepopulate the database with few groups and permissions?
If you can associate those custom permissions with your models, try defining them under the permissions attribute inside model's Meta class.
Create fixtures for your custom groups, but use natural keys when serializing them, so that they refer to related permissions by their name rather than pk.
Related
I have an existing model that looks somewhat like the following...
class Resource(models.Model):
id = models.AutoField(primary_key=True)
We have been using this for some time, and now have ~1M instances of these Resource objects (and associated ForeignKey/else usages) in our database.
I now have a need to track another ID on this model, one that I want to enforce is unique.
other_id = models.IntegerField(unique=True)
This other_id information is currently stored in some external CSVs, and I want to (at some point in the process) load this information in to all existing Resource instances.
After adding the above field, Django's makemigrations works just fine. However when I go to apply said migration against an existing database I get an error indicating that I need to provide a default to use for all existing Resource instances. I'm sure many of you have seen something similar.
What is the best approach to getting around this limitation? Some methods I have thought of...
Remove the unique=True requirement
apply the migration
externally load in the other_id value to all existing models (through some management command, or 1-off script)
add the unique=True back in and apply the migration
Dump all existing data to JSON
flush all tables
apply the migration (with unique=True)
write a script that loads the data back in, adding the correct other_id value
(unsure if this is possible) - Write some custom migration logic to automatically reference these external CSVs to load other_id values when I run manage.py migrate. This could hit issues if (at some point in the future) someone re-runs these migrations and this part fails (cannot find existing resource id in the CSVs to pull out other_id).
All of these feel complicated, but then again I guess what I am trying to do isn't the simplest thing either.
Any ideas? I have to imagine someone has had to work around a similar issue in the past.
Thanks!
Actually, the source or your issue is not the unique constraint by itself but the fact that your field doesn't allow nulls and has no default value - you'd have the very same error with a non-unique field.
The proper solution here is to allow the field to be null (null=True) and default it to None (which will translate to sql "null"). Since null values are excluded from unique constraints (at least if your db vendor respects SQL standard), this allow you to apply the schema change while still making sure you cannot have a duplicate for non-null values.
Then you may want a data migration to load the known "other_id" values, and eventually a third schema migration to disallow null values for this field - if and only if you know you have filled this field for all records.
Django has something called Data Migrations where you create a migration file that modifies/remove/add data to your database as you apply your migrations.
In this case you would create 3 different migrations:
Create a migration that allow null values with null=True.
Create a data migration that populate the data.
Create a migration that disallow null values by removing the null=True added in step 1.
As you then run python manage.py migrate it would apply all of the migrations in step 1-3 in the correct order.
Your data migration would look something like this:
from django.db import migrations
def populate_reference(apps, schema_editor):
MyModel = apps.get_model('yourappname', 'MyModel')
for obj in MyModel.objects.all():
obj.other_id = random_id_generator()
obj.save()
class Migration(migrations.Migration):
dependencies = [
('yourappname', '0001_initial'),
]
operations = [
migrations.RunPython(populate_reference),
]
You can create an empty migration file using the ./manage.py makemigrations --empty yourappname command.
I want to write my own permissions in Django, I mean I want to define exactly what a user can or cannot do, I have read this enter link description here
but it seems change_task_status is sth predefined in Django. for example, I want to define exactly users can have access to just get method of a view and just from row 1 to 8 of the database table, and sth like this. How can I do this?
Edit:
First of all, I did this with default permissions that are in auth_permission table in Django, for each model it creates permissions of add/view/change/delete in this table and I know that I can use it for my purpose. but I have two problems, first I don't want to use the default permission class od Django.contrib,auth model thus I want to create my own permission table (instead of auth_permissions I have mapp_permissions) it makes a problem for me now this new table is not filled with default permissions so I need to define permissions myself I mean I have to say what add_modelname means and also after I do this I need to define some new permissions that say for example for one model:user_x have permission view_modelname, users also have this permission but from data of this model which stored in database user_y just can see records of 1 to 8 of db table not all
Edit 2:
as you can see in permissions class comment it says:"it's
not currently possible to say "Mary may only change news stories that have a
certain status or publication date.""
how can I make it possible?
also, there should be a code inside Django files that define for the machine for example add_user which is in the table means what
According to Edit 2 , I see that you have some business logic related to permissions check , have a look at django-rules , I think it's what you're looking for.
We are migrating the data in several instances of our Django project to a new schema.
The old schema had:
class Group(models.Model)
class User(models.Model)
And the new schema has:
class AccessEntity(models.Model)
class Group(AccessEntity)
class User(AccessEntity)
We are trying to use South to do a data migration for these groups and users. http://south.aeracode.org/docs/tutorial/part3.html
I've gathered that I'll need to use forward rules to specify how to migrate the Users but there are a few issues I've run up against.
The main issue is how to keep the ID of the User/Group the same if I were to create a new User object that extends the AccessEntity class.
Users & Groups are referenced to by objects they own or are assigned to them. If I change their ID that information would be lost. Is there a way of keeping the same ID for an object even though I need it to now extend from AccessEntity?
not sure if I understand your question correctly, but the way multi-table model inheritance works ist that there will be an implicit one-to-one field in the parent and child models. So both User and Group would use an ID field of AccessEntity if AccessEntity has such a field.
If you create AccessEntity such that it has a field ID you can assign to it when you write a forward (data)-migration. That way you can make sure that the AccessEntity gets the right ID.
If have written a longer multi-table inheritance tutorial and it looks like you are trying to do something similar.
And furthermore the answer to this question could also be helpful (note that some things in the original answer does will not work in new versions of django / south, see my tutorial / the answer at the bottom for changes).
What might be a problem in your case is that if you already have data in both User and Groups and the id field is auto-generated, IDs likely not be distinct, e.g. you are likely going to have both a User and a Group with ID==1. This could be a problem if you want to query based on those IDs and of course ID could not be a primary key for AccessEntity then.
We're running django alongside - and sharing a database with - an existing application. And we want to use an existing "user" table (not Django's own) to store user information.
It looks like it's possible to change the name of the table that Django uses, in the Meta class of the User definition.
But we'd prefer not to change the Django core itself.
So we were thinking that we could sub-class the core auth.User class like this :
class OurUser(User) :
objects = UserManager()
class Meta:
db_table = u'our_user_table'
Here, the aim is not to add any extra fields to the customized User class. But just to use the alternative table.
However, this fails (likely because the ORM is assuming that the our_user_table should have a foreign key referring back to the original User table, which it doesn't).
So, is this sensible way to do what we want to do? Have I missed out on some easier way to map classes onto tables? Or, if not, can this be made to work?
Update :
I think I might be able to make the change I want just by "monkey-patching" the _meta of User in a local_settings.py
User._meta.db_table = 'our_user_table'
Can anyone think of anything bad that could happen if I do this? (Particularly in the context of a fairly typical Django / Pinax application?)
You might find it useful to set up your old table as an alternative authentication source and sidestep all these issues.
Another option is to subclass the user and have the subclass point to your user-model. Override the save function to ensure that everything you need to do to preserve your old functionality is there.
I haven't done either of these myself but hopefully they are useful pointers.
Update
What I mean by alternative authentication in this case is a small python script that says "Yes, this is a valid username / password" - It then creates an instance of model in the standard Django table, copies fields across from the legacy table and returns the new user to the caller.
If you need to keep the two tables in sync, you could decide to have your alternative authentication never create a standard django user and just say "Yes, this is a valid password and username"
I have a legacy database with an integer set as a primary key. It was initially managed manually, but since we are wanting to move to django, the admin tool seemed to be the right place to start. I created the model and am trying to set the primary key to be an autofield. It doesn't seem to be remembering the old id in updates, and it doesn't create new id's on insert. What am I doing wrong?
The DB is responsible for managing the value of the ID. If you want to use AutoField, you have to change the column in the DB to use that. Django is not responsible for managing the generated ID