I'm just starting with Python and Django and have an existing database. I'd like to create views to edit the fields in this database, do I have to create models to match these tables or is there a way to start editing after connecting the database and a view?
You can manage the schema manually, for example:
class Person(models.Model):
id = models.IntegerField(primary_key=True)
first_name = models.CharField(max_length=70)
class Meta:
managed = False
db_table = 'CENSUS_PERSONS'
This can be an option, but I would recommend to let Django ORM manage them, by creating the models from scratch and doing a database migration.
More details: https://docs.djangoproject.com/en/3.0/howto/legacy-databases/
I think I have a more or less unorthodox and hackish question for you. What I currently have is django project with multiple apps.
I want to use a non-abstract model (ModelA) of one app (app1) and use it in another app (app2) by subclassing it. App1's models
should not be migrated to the DB, I just want to use the capabilities of app1 and it's model classes, by extending its functionality and logic.
I achieved that by adding both apps to settings.INSTALLED_APPS, and preventing app1's models being migrated to the DB.
INSTALLED_APPS += (
'App1',
'App2',
)
# This is needed to just use App1's models
# without creating it's database tables
# See: http://stackoverflow.com/a/35921487/1230358
MIGRATION_MODULES = {
'App1': None,
}
So far so good, ugly and hackish, I know... The remaining problem is now that most of app1's models are non-abstract (ModelA) and if I try
to subclass them, none of the ModelA's fields get populated to the db into the table named app2_modelb. This is clear to me, because I excluded the App1 from
migrating to the DB and therefore the table app1_modela is completely missing in the DB.
My idea now was to clone ModelA, preserve all its functionallity, and changing it's Meta information from non-abstract to abstract (ModelB.Meta.abstract = True).
I hope that by this, all the original fields of ModelA will be inherited to ModelB and can be found in its respective DB table and columns (app1_modelb).
What I have right now is:
# In app1 -> models.py
class ModelA(models.Model):
title = models.CharField(_('title'), max_length=255)
subtitle = models.CharField(_('subtitle'), max_length=255)
class Meta:
abstract = False # just explicitly for demonstration
# In app2 -> models.py
from app1.models import ModelA
class ModelB(ModelA):
pass
# Just extending ModelAdoes not create the fields title and subtitle fields in app2_modelb
# because ModelA.meta.abstract = False
My current way (pseudo code) to make an existing non-abstract model abstract looks like this:
# In app2 -> models.py
from app1.models import ModelA
def get_abstract_class(cls):
o = dict(cls.__dict__)
o['_meta'].abstract = True
o['_meta'].app_label = 'app2'
o['__module__'] = 'app2.models'
#return type('Abstract{}'.format(cls.__name__), cls.__bases__, o)
return type('Abstract{}'.format(cls.__name__), (cls,), o)
ModelB = get_abstract_class(ModelA)
class ModelC(ModelB):
# title and subtitle are inherited from ModelA
description = models.CharField(_('description'), max_length=255)
This does not work, and after this lengthy description my (simple) question would be, if and how is it possible to clone a non-abstract model class preserving all its functionality and how to change it to be abstract?
Just to be clear. All upper fuzz is about, that I can't change any code in app1. May it be that app1 is a django app installed via pip.
Why not, in app1
AbstractBaseModelA(models.Model):
# other stuff here
class Meta:
is_abstract=True
ModelA(AbstractBaseModelA):
# stuff
in app2:
MobelB(AbstractBaseModelA):
# stuff
Sorry if I've misunderstood your aims, but I think the above should achieve the same end result.
I have a very similar situation like this: Django migration strategy for renaming a model and relationship fields
I need to rename Foo to Bar.
We have an identical myapp:
class Foo(models.Model):
name = models.CharField(unique=True, max_length=32)
description = models.TextField(null=True, blank=True)
But I have in my myotherapp a ManyToMany field:
class AnotherModel(models.Model):
foo = models.ForeignKey(Foo)
is_awesome = models.BooleanField()
class YetAnotherModel(models.Model):
foo = models.ManyToManyField(Foo, blank=True, null=True) # Here!
is_ridonkulous = models.BooleanField()
I tried rename:
foo = models.ManyToManyField(Foo, blank=True, null=True)
to IntegerField() but doesn't work. How can I do that?
This is how I did it. Note: I was NOT in production, so I did not have to worry about information already in the tables. If you currently have data that you need to keep in the linking table, back up your data first. Also, I was using Django 1.9, but I think that everything referenced here is in 1.8 too.
The issue with the many-to-many relationship is the intermediate tables. Using RemoveField and AddField handled that.
The myapp migration for the model rename probably looks something like this:
class Migration(migrations.Migration):
dependencies = [
('Foo', '0001_initial.py'),
]
operations = [
migrations.RenameModel(
old_name='Foo',
new_name='Bar',
),
]
Next you would run:
python manage.py makemigrations --empty myotherapp
Then you would put this code in the new migration:
dependencies = [
('myotherapp', '0001_initial.py'),
('myapp', '0002_whateverthismigrationwasnamed')
]
operations = [
migrations.RemoveField(
model_name='YetAnotherModel',
name='Foo'
),
migrations.AddField(
model_name='YetAnotherModel',
name='Bar',
field=models.ManyToManyField(blank=True, null=True, to='myapp.Bar'),
),
]
It's important to make sure that you add the rename model migration from myapp as a dependency so it runs first.
Now, if you are in production you should NOT use this method without taking precautions. It straight up deletes the linking table. This from the django docs on RemoveField:
Bear in mind that when reversed, this is actually adding a field to a model. The operation is reversible (apart from any data loss, which of course is irreversible) if the field is nullable or if it has a default value that can be used to populate the recreated column.
If you are in production, you will want to take steps to backup the data so you can restore it into the new table.
I'm trying to create a custom auth_users table with Django 1.5 following the intructions here:
https://docs.djangoproject.com/en/1.5/topics/auth/customizing/#auth-custom-user
But the code below doesn't seem to be working even though I have set the new columns it should contain. It still creates the same default table even after dropping the full database and python manage.py syncdb
I have read several documents and that is what I have right now:
models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser
class User(AbstractBaseUser):
identifier = models.EmailField(max_length=254)
first_name = models.CharField(max_length=254)
last_name = models.CharField(max_length=254)
USERNAME_FIELD = 'identifier'
REQUIRED_FIELDS = ['first_name','last_name']
This models.py is installed in settings.py
settings.py
AUTH_PROFILE_MODEL = 'myaccount.User'
This doesn't make any effect to the table, but it creates a new one called myaccount_user.
What's wrong with that? What should I change in order to modify auth_user table instead of creating a new one?
The error is that should be AUTH_PROFILE_MODEL = 'myaccount.User' instead of AUTH_PROFILE_MODULE = 'myaccount.User'
Also you forgot a bunch of methods in your Model and a USERNAME_FIELD
see a full example of how to build your own CUSTOM USER
Is there a way to extend the built-in Django Group object to add additional attributes similar to the way you can extend a user object? With a user object, you can do the following:
class UserProfile(models.Model):
user = models.OneToOneField(User)
and add the following to the settings.py file
AUTH_PROFILE_MODULE = 'app.UserProfile'
which gets you:
profile = User.objects.get(id=1).get_profile()
Is there any equivalent to this approach for extending a group? If not, is there an alternative approach I can take?
If you simply subclass the Group object then by default it will create a new database table and the admin site won't pick up any new fields.
You need to inject new fields into the existing Group:
if not hasattr(Group, 'parent'):
field = models.ForeignKey(Group, blank=True, null=True, related_name='children')
field.contribute_to_class(Group, 'parent')
To add methods to the Group, subclass but tag the model as proxy:
class MyGroup(Group):
class Meta:
proxy = True
def myFunction(self):
return True
You can create a model that subclasses Group, add your own fields, and use a Model Manager to return any custom querysets you need. Here's a truncated example showing how I extended Group to represent Families associated with a school:
from django.contrib.auth.models import Group, User
class FamilyManager(models.Manager):
"""
Lets us do querysets limited to families that have
currently enrolled students, e.g.:
Family.has_students.all()
"""
def get_query_set(self):
return super(FamilyManager, self).get_query_set().filter(student__enrolled=True).distinct()
class Family(Group):
notes = models.TextField(blank=True)
# Two managers for this model - the first is default
# (so all families appear in the admin).
# The second is only invoked when we call
# Family.has_students.all()
objects = models.Manager()
has_students = FamilyManager()
class Meta:
verbose_name_plural = "Families"
ordering = ['name']
def __unicode__(self):
return self.name
I managed to use migrations with #Semprini aswer.
So i needed to create a company related field in my groups related field, so in my models i did this:
if not hasattr(Group, 'company'):
field = models.ForeignKey(Company, on_delete=models.DO_NOTHING, null=True)
field.contribute_to_class(Group, 'company')
class Group(Group):
class Meta:
proxy = True
Then i run manage.py makemigrations. This created 2 files. One with dependencies on the other, but the first one belonging to the auth app was created inside my virtual enviroment. The files look like this:
# Generated by Django 2.2.5 on 2019-10-08 16:00
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('myapp', '0013_guestuser_permissions_20190919_1715'),
('auth', '0011_update_proxy_permissions'),
]
operations = [
migrations.AddField(
model_name='group',
name='company',
field=models.ForeignKey(
null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='myapp.Company'),
),
]
The second one created in myapp migrations folder look like this:
# Generated by Django 2.2.5 on 2019-10-08 16:00
import django.contrib.auth.models
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('auth', '0012_group_company_20191008'),
('myapp', '0013_guestuser_permissions_20190919_1715'),
]
operations = [
migrations.CreateModel(
name='Group',
fields=[
],
options={
'proxy': True,
'indexes': [],
'constraints': [],
},
bases=('auth.group',),
managers=[
('objects', django.contrib.auth.models.GroupManager()),
],
),
]
So the solution was to move the file created in my virtualenv to myapp migrations folder, before the other one generated with makemigrations, but since the migration is applied to the auth app instead of myapp i have to implement a workaround in the file. So the final file now is:
# Generated by Django 2.2.5 on 2019-10-08 16:00
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('myapp', '0013_guestuser_permissions_20190919_1715'),
('auth', '0011_update_proxy_permissions'),
]
operations = [
migrations.AddField(
model_name='group',
name='company',
field=models.ForeignKey(
null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='myapp.Company'),
),
]
def mutate_state(self, project_state, preserve=True):
"""
This is a workaround that allows to store ``auth``
migration outside the directory it should be stored.
"""
app_label = self.app_label
self.app_label = 'auth'
state = super(Migration, self).mutate_state(project_state, preserve)
self.app_label = app_label
return state
def apply(self, project_state, schema_editor, collect_sql=False):
"""
Same workaround as described in ``mutate_state`` method.
"""
app_label = self.app_label
self.app_label = 'auth'
state = super(Migration, self).apply(project_state, schema_editor, collect_sql)
self.app_label = app_label
return state
The mutate an apply methods allow you to migrate to the auth app from myapp migrations.
In the second file i just change the dependencie to depend on the newly file created:
# Generated by Django 2.2.5 on 2019-10-08 16:00
import django.contrib.auth.models
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('myapp', '0014_group_company_20191008'),
('myapp', '0013_guestuser_permissions_20190919_1715'),
]
operations = [
migrations.CreateModel(
name='Group',
fields=[
],
options={
'proxy': True,
'indexes': [],
'constraints': [],
},
bases=('auth.group',),
managers=[
('objects', django.contrib.auth.models.GroupManager()),
],
),
]
For me worked solution based on:
https://docs.djangoproject.com/pl/1.11/topics/auth/customizing/#extending-user
Let me explain what I did with Groups extending default model with email alias:
First of all I created my own django application let name it
python manage.py startapp auth_custom
Code section:
In auth_custom/models.py I created object CustomGroup
from django.contrib.auth.models import Group
from django.db import models
class CustomGroup(models.Model):
"""
Overwrites original Django Group.
"""
def __str__(self):
return "{}".format(self.group.name)
group = models.OneToOneField('auth.Group', unique=True)
email_alias = models.EmailField(max_length=70, blank=True, default="")
In auth_custom/admin.py:
from django.contrib.auth.admin import GroupAdmin as BaseGroupAdmin
from django.contrib.auth.models import Group
class GroupInline(admin.StackedInline):
model = CustomGroup
can_delete = False
verbose_name_plural = 'custom groups'
class GroupAdmin(BaseGroupAdmin):
inlines = (GroupInline, )
# Re-register GroupAdmin
admin.site.unregister(Group)
admin.site.register(Group, GroupAdmin)
After making migrations I have such result in Django Admin view.
Custom Group in Django Admin
In order to access this custom field you must type:
from django.contrib.auth.models import Group
group = Group.objects.get(name="Admins") # example name
email_alias = group.customgroup.email_alias
If any mistakes please notify me, I'll correct this answere.