I have a model for categories, which contains a circular foreign key. I dumped all the data from this model and I created a data migration with django-south for loading them into a different DBMS, but I having a lot of problem doing it, because of this circular dependency.
This is the model I'm referring:
class Category(MPTTModel):
name = models.CharField(_('name'), max_length=50, unique=True)
parent = models.ForeignKey('self', null=True, blank=True, related_name='categories')
description = models.TextField(_('description'), blank=True, null=True)
created_on = models.DateTimeField(auto_now_add = True, default=date.today())
updated_on = models.DateTimeField(auto_now = True, default=date.today())
def __unicode__(self):
return "%s" %(self.name)
class Meta:
verbose_name = _('category')
verbose_name_plural= _('categories')
Thanks to this post I could find a solution. Temporarily disable foreign key checks while you load in data is the best feasible solution for this issues.
Since Django doesn't provide a way to do this, we should execute raw sql code.
So, I created a data migration with django-south and the rest is in the code below:
class Migration(DataMigration):
def forwards(self, orm):
#~ Disable foreign key checks during fixture loading
from django.db import connections, DEFAULT_DB_ALIAS
connection = connections[DEFAULT_DB_ALIAS]
if 'mysql' in connection.settings_dict['ENGINE']:
cursor = connection.cursor()
cursor.execute('SET foreign_key_checks = 0')
#~ Load fixture
from django.core.management import call_command
call_command('loaddata', 'categories_fixture.json', verbosity=0)
#~ Enable foreign key checks after fixture loading
if 'mysql' in connection.settings_dict['ENGINE']:
cursor = connection.cursor()
cursor.execute('SET foreign_key_checks = 1')
connection.close()
The quick answer is that you need to disable foreign key constraints while loading.
There's a patch for Django, but it may or may not be in the version you are using:
https://code.djangoproject.com/ticket/3615
Alternatively, don't use fixtures, use SQL: https://docs.djangoproject.com/en/dev/howto/initial-data/#providing-initial-sql-data
The upside is that you can do anything in a model SQL file that you can do in SQL. The downside is that it is no longer database-agnostic, depending on the SQL you are using.
Answering in 2016. Django supports loading interdependent fixtures, simply add them to the same file
// fixtures.json
[
{
"model": "A",
"pk": 1100,
"fields": {
"bfk": 1000,
}
},
{
"model": "B",
"pk": 1000,
"fields": {
"Afk": 1100
}
}
]
Related
I am using legacy DB tables that I have no control over the tables design.
Table A has an auto_gen ID field as follows:
Table DRAWER (One Side), Table FOLDER (Many Side)
class Drawer(models.Model):
id = models.PositiveIntegerField(primary_key=True, db_column='ID')
fld1 = models.CharField(max_length=1000, db_column='FLD1')
fld2 = models.CharField(max_length=1000, db_column='FLD2')
fld3 = models.CharField(max_length=1000, db_column='FLD3')
def __str__(self):
field_values = []
for field in self._meta.get_fields():
field_values.append(str(getattr(self, field.name, '')))
return ' '.join(field_values)
class Meta:
managed = False
db_table = u'"MySchema"."DRAWER"'
class Folder(models.Model):
id = models.PositiveIntegerField(primary_key=True, db_column='ID')
fld1 = models.CharField(max_length=1000, db_column='FLD1')
fld4 = models.CharField(max_length=1000, db_column='FLD4')
fld5 = models.CharField(max_length=1000, db_column='FLD5')
drawer = models.ForeignKey(to=Drawer, related_name='Drawer.fld1+', on_delete=models.CASCADE, db_constraint=False)
def __str__(self):
field_values = []
for field in self._meta.get_fields():
field_values.append(str(getattr(self, field.name, '')))
return ' '.join(field_values)
class Meta:
managed = False
db_table = u'"MySchema"."FOLDER"'
The ID fields are numeric auto gen. I am trying to use Djano models to establish a one to many relation between Tables DRAWER and FOLDER using fld1. fld1 is unique in Drawer, and many in Folder. I have done this in Java, but so far, it seems that Django won't allow it. Django seems to expect that one side must be a PK? I do realize that Django has many to one (instead of one to many) and followed the documentations ... but it is not working. Please advise if there is a way to do this in Django models framework.
The end result I am looking for in the rest service is something like below, which is what I get from the current JAVA Hibernate Data Rest Service:
{
"ID": 1234,
"FLD1": "xxxxxxxx",
"FLD2": "wertt",
"FLD3": "rtyuio",
folder:[
{
"ID": 5645,
"FLD1": "xxxxxxxx",
"FLD4": "zzzzzzz",
"FLD5": "cccccccc",
},
{
"ID": 5645,
"FLD1": "xxxxxxxx",
"FLD4": "rrrrrrr",
"FLD5": "cccccuuuuuuuuuuccc",
}
.
.
]
I appreciate any help that you can provide.
I'm developing a web app using Django REST as the backend, and Angular as the frontend.
I have a legacy database (read-only) that has 3 tables that I want to join together to get all the data I need.
Basically, I'm using the result of this query to auto-populate a form. The user will enter a known meter number into the form and then click a button that will pull the other related information into the form - minimizing data entry.
Simplified database tables and fields
MeterNumber
bi_acct
bi_srv_loc_nbr
bi_mtr_nbr
Customer
bi_acct (join to MeterNumber table)
other fields I need
ServiceLocation
bi_srv_loc_nbr (join to MeterNumber table)
other fields I need
So, as you can see, these tables can all join to the MeterNumber table based on the bi_acct and bi_srv_loc_nbr fields. I want to be able to make a REST call to something like:
server/meter_number/123456
And then the JSON response would be something like:
{
"bi_acct": 111111,
"bi_srv_loc_nbr": 222222,
"bi_mtr_nbr": 123456,
"customer": [
{
"bi_acct": 111111,
"needed_field": "... "
}
],
"service_location": [
{
"bi_srv_loc_nbr": 222222,
"needed_field": "..."
}
]
}
And this response would be used to auto-populate the form.
But how do I create these joins and have the data in the JSON response? Can I do it using serializers?
Models:
class MeterNumber(models.Model):
bi_acct = models.IntegerField(
db_column='BI_ACCT')
bi_srv_loc_nbr = models.IntegerField(
db_column='BI_SRV_LOC_NBR'
bi_mtr_nbr = models.IntegerField(
db_column='BI_MTR_NBR', blank=True, null=True)
class Meta:
app_label = 'cis'
db_table = 'MeterNumber'
managed = False
def __str__(self):
return str(self.bi_acct)
class Customer(models.Model):
bi_acct = models.IntegerField(
db_column='BI_ACCT')
other_fields = ...
class Meta:
app_label = 'cis'
db_table = 'Customer'
managed = False
def __str__(self):
return str(self.bi_acct)
class ServiceLocation(models.Model):
bi_srv_loc_nbr = models.IntegerField(
db_column='BI_SRV_LOC_NBR')
other_fields = ...
class Meta:
app_label = 'cis'
db_table = 'ServiceLocation'
managed = False
def __str__(self):
return str(self.bi_acct)
Put more simply, given a meter number I want to join the Customer and ServiceLocation tables to the MeterNumber. This is a very simple SQL join.
Any guidance is appreciated!
Unfortunately best practices of ORM doesn't give flexibility of SQL, no foreign key implies no joins so you'll have to fall back to raw SQL queries, below is a example snippet. Modify the SQL query according to your need.
from django.db import connection
def my_custom_sql():
cursor = connection.cursor()
cursor.execute("select * from Customer")
row = cursor.fetchone()
return row
I think you should use ForeignKeys, so try change MeterNumber:
class MeterNumber(models.Model):
bi_acct = models.ForeignKey('Customer')
bi_srv_loc_nbr = models.ForeignKey('ServiceLocation')
bi_mtr_nbr = models.IntegerField(
db_column='BI_MTR_NBR', blank=True, null=True
)
I want to add some default values in my database when the related model is creating with makemigrations command.
For example I have this as model;
class BaseModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True, verbose_name='Created Date')
modified_at = models.DateTimeField(auto_now=True, verbose_name='Update Date')
is_deleted = models.BooleanField(default=False, verbose_name='Deleted')
class Meta:
abstract = True
class ModelType(BaseModel):
description = models.CharField(verbose_name='Name', max_length=225 )
and as I said before I want to add some default values ("value1", "value2", "value3", "value4") for my ModelType table. Is that possible?
If you want to always add the default data when you execute a given migration, the safest way is to use a datamigration (as suggested by #Kos).
To create a data migration, use ./manage.py makemigrations <app_label> --empty and manually add the required code to populate the data.
I normally use a custom operation which executes a get_or_create on the specified model. Add this code to either the migration file itself or somewhere where it can be imported from:
from django.db import migrations
def noop(apps, schema_editor):
pass
class EnsureInstanceCreated(migrations.RunPython):
def __init__(self, app_name, model_name, attrs, defaults=None):
super(EnsureInstanceCreated, self).__init__(self.add_instance, noop)
self.app_name = app_name
self.model_name = model_name
self.attrs = attrs
self.defaults = defaults
def add_instance(self, apps, schema_editor):
Model = apps.get_model(self.app_name, self.model_name)
Model.objects.get_or_create(
defaults=self.defaults,
**self.attrs
)
Then, in the migration itself:
from django.db import migrations
from myproject.utils.migrations import EnsureInstanceCreated
class Migration(migrations.Migration):
dependencies = [
('myproject', '000x_auto_...'),
]
operations = [
EnsureInstanceCreated('myapp', 'ModelType', attrs={
'description': 'value1',
}, defaults={
# ...
}),
EnsureInstanceCreated('myapp', 'ModelType', attrs={'description': 'value2'}),
EnsureInstanceCreated('myapp', 'ModelType', {'description': 'value3'}),
]
Apparently there is a way. You can use Fixtures to initialize models with data.
Refer to this piece of documentation: https://docs.djangoproject.com/en/1.10/howto/initial-data/
I have below model:
class Property(models.Model):
job = models.ForeignKey(Job, on_delete=models.CASCADE)
app = models.ForeignKey(App, on_delete=models.CASCADE)
name = models.CharField(max_length=120)
value = models.CharField(max_length=350, blank=True)
description = models.TextField(blank=True)
pub_date = models.DateTimeField('date_published', default=timezone.now)
class Meta:
verbose_name_plural = "properties"
unique_together = (('value', 'name'),)
def __str__(self):
return self.name
When I try to create a Property object in admin page (I'm using Django Suit) with name/value which are already exist I get the exception: "Property with this Value and Name already exists." So it works perfect.
But in manage.py shell:
>>>from myapp.models import App, Property, Job
>>>from django.shortcuts import get_object_or_404
>>>app = get_object_or_404(App, app_name='BLABLA')
>>>job = get_object_or_404(Job, job_name='BLABLA2')
>>> Property.objects.create(job=job, app=app, name='1', value='1')
<Property: 1>
>>> Property.objects.create(job=job, app=app, name='1', value='1')
<Property: 1>
In this case I do not get any exceptions and objects are added in database.
I tried makemigrations, migrate and migrate --run-syncdb.
Django 1.9.12, sqlite3
The unique constraints are enforced at database level. You're not getting any error probably because SQLite doesn't support this type of constraint. You cannot add constraint to existing table in SQLite. If you're in early development stage, drop the table and recreate it with updated constraints. Then it should work fine in shell.
Check SQLite alter table docs for allowed updates on an existing table.
The admin form throws error because it checks uniqueness by itself without relying on database constraints.
I am having difficulty serializing a django object. The problem is that there are foreign keys. I want the serialization to have data from the referenced object, not just the index.
For example, I would like the sponsor data field to say "sponsor.last_name, sponsor.first_name" rather than "13".
How can I fix my serialization?
json data:
{"totalCount":"2","activities":[{"pk": 1, "model": "app.activity", "fields": {"activity_date": "2010-12-20", "description": "my activity", "sponsor": 13, "location": 1, ....
model code:
class Activity(models.Model):
activity_date = models.DateField()
description = models.CharField(max_length=200)
sponsor = models.ForeignKey(Sponsor)
location = models.ForeignKey(Location)
class Sponsor(models.Model):
last_name = models.CharField(max_length=20)
first_name= models.CharField(max_length=20)
specialty = models.CharField(max_length=100)
class Location(models.Model):
location_num = models.IntegerField(primary_key=True)
location_name = models.CharField(max_length=100)
def activityJSON(request):
activities = Activity.objects.all()
total = activities.count()
activities_json = serializers.serialize("json", activities)
data = "{\"totalCount\":\"%s\",\"activities\":%s}" % (total, activities_json)
return HttpResponse(data, mimetype="application/json")
Add relations to the serializer like this:
activities_json = serializers.serialize("json", activities, relations=('sponsor',))
Then all you need is:
return HttpResponse(activities_json, mimetype="application/json")
Then make sure you also have the django library wadofstuff installed.
Hope this helps!
The docs seem to explain exactly how to do this. Read the part about serialization of natural keys.
This small lib is very handy with django : http://code.google.com/p/wadofstuff/wiki/DjangoFullSerializers
It allows more customisation than the standard encoder.
any2any also contains serializers allowing to customize completely the output format :
check the docs
repo on bitbucket
or on pypi