Given a database table that was created using this SQL query:
CREATE TABLE Bill
(
Time DATE NOT NULL ,
Address VARCHAR2 (60) NOT NULL ,
ID NUMBER NOT NULL
) ;
ALTER TABLE Bill ADD CONSTRAINT Bill_PK PRIMARY KEY ( ID ) ;
CREATE SEQUENCE Bill_ID_SEQ START WITH 1 NOCACHE ORDER ;
CREATE OR REPLACE TRIGGER Bill_ID_TRG BEFORE
INSERT ON Paragony FOR EACH ROW BEGIN :NEW.ID := Bill_ID_SEQ.NEXTVAL;
END;
I have to use it with Django ORM so I have run inspectdb command. It is the autogenerated code:
class Bill(models.Model):
time = models.DateField()
address = models.CharField(max_length=60)
id = models.IntegerField(primary_key=True)
class Meta:
managed = False
db_table = 'bill'
After saving it to app' models.py file and running migrations everything was fine. I could read DB like it was created using ORM. However there was a problem with creating rows in Bill table.
It is simple form for Bill model:
class BillForm(ModelForm):
class Meta:
model = Bill
fields = ('time', 'address')
The problem is that I can't retrieve the ID generated with the DB sequence. Adding id field to Form won't work because we have to generate it with code and then pass as a argument. Even than database will create different ID that will not be possible to retrieve without raw queries.
The solution is really simple. We have to remove the id field from Bill model:
class Bill(models.Model):
time = models.DateField()
address = models.CharField(max_length=60)
# Remove 'id' field so the ORM can work properly.
# id = models.IntegerField(primary_key=True)
class Meta:
managed = False
db_table = 'bill'
This way the Django ORM can make all the hard work and correctly use legacy database sequences. Bill objects now can be created using BillForm and they will have proper IDs.
Related
I am using a SQL Server table with period columns, and my model in Django is defined as
class Client(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(unique=True, max_length=40)
sys_start_time = models.DateTimeField(db_column="SysStartTime")
sys_end_time = models.DateTimeField(db_column="SysEndTime")
class Meta:
managed = False
db_table = "client"
where sys_start_time and sys_end_time are period columns. Due to sql server rules, any query defining values for these columns are invalid, which means that the sql query django generated when creating or updating this model won't work.
I managed to exclude them in update_fields when performing update so that django will generate queries without period columns, but I can't use the same update_fields parameter when creating an object, otherwise I get an error saying
Cannot force both insert and updating in model saving.
Is there a way to let django ignore certain fields when creating an object?
OR
Is there a way to tell Django to send DEFAULT (sql server keyword, not string) as the value for these period columns?
For anyone who faces the same problem, I ended up overriding _do_insert() method in the model to take out period columns out from query when creating new records
class Client(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(unique=True, max_length=40)
sys_start_time = models.DateTimeField(db_column="SysStartTime")
sys_end_time = models.DateTimeField(db_column="SysEndTime")
class Meta:
managed = False
db_table = "client"
def _do_insert(self, manager, using, fields, returning_fields, raw):
period_columns = ["sys_start_time", "sys_end_time"]
non_ignored_fields = [
f for f in fields if (f.name not in period_columns and f.attname not in period_columns )
]
return super(ModelWithSaveIgnoreFields, self)._do_insert(
manager, using, non_ignored_fields, returning_fields, raw
)
For update, defining update_fields still works perfectly.
Thank you for reading i am new to Django and
For my application it's a requirement to create a table for each client to store his data.
Each client table has the same data so i made a Model for it.
But the table name is variable:
i tried
Models.py
class Meta:
abstract = True
organisation = models.CharField(max_length=255, null=True, blank=True,)```
Model
```class Test(DatIm):
class Meta(DataIm.Meta):
db_table = DatIm.organisation```
in the view:
```Test.objects.update_or_create(organisation=row['organisation'])```
but it doesnt create the table and gives errors.
i would expect to
creates or get the table for organisation
insert model.save data in organisation Db
I need for each organisation an table because a lot of data.
I have migrated over 10,000 records from my old mySQL database to Django/sqlite. In my old mysql schema's Song table, the artist field was not a 1 to many field but was just a mysql varchar field. In my new Django model, I converted the artist field to a ForeignKey and used temp_artist to temporarily store the artist's name from the old database.
How do I create each Song instance's artist foreignkey based on the temp_artist field? I'm assuming I should use the manager's get_or_create method but where and how do I write the code?
my model below:
class Artist (models.Model):
name = models.CharField(max_length=100)
class Song (models.Model):
artist = models.ForeignKey(Artist, blank=True, null=True, on_delete=models.CASCADE, verbose_name="Artist")
temp_artist = models.CharField(null=True, blank=True, max_length=100)
title = models.CharField(max_length=100, verbose_name="Title")
duration = models.DurationField(null=True, blank=True, verbose_name="Duration")
You can write a custom management command that performs this logic for you. The docs provide good instructions on how to set it up. Your command code would look something like this:
# e.g., migrateauthors.py
from django.core.management.base import BaseCommand
from myapp import models
class Command(BaseCommand):
help = 'Migrate authors from old schema'
def handle(self, *args, **options):
for song in myapp.models.Song.objects.all():
song.artist, _ = models.Artist.objects.get_or_create(name=song.temp_artist)
song.save()
Then you simply run the management command with manage.py migrateauthors. Once this is done and verified you can remove the temporary field from your model.
Since you don't have a usable foreign key at the moment you would have to dig down to raw_sql. If you were still on mysql you could have used the UPDATE JOIN syntax. But unfortunately Sqlite does not support UPDATE JOIN.
Luckily for you you have only a few thousand rows and that makes it possible to iterate through them and update each row individually.
raw_query = '''SELECT s.*, a.id as fkid
FROM myapp_song s
INNER JOIN myapp_artist a on s.temp_artist = a.name'''
for song in Song.objects.raw(raw_query)
song.artist_id = s.fkid
song.save()
This might take a few minutes to complete because you don't have an index on temp_artist and name. Take care to replace myapp with the actual name of your app.
Edit1:
Though Sqlite doesn't have update JOIN, it does allow you to SET a value with a subquery. So this will also work.
UPDATE myapp_song set artist_id =
(SELECT id from myapp_artist WHERE name = myapp_song.temp_artist)
type it in the sqlite console or GUI. Make sure to replace myapp with your own app name. This will be very quick because it's a single query. All other solutions including my alternative solution in this answer involve 10,000 queries.
Edit 2
If your Artist table is empty at the moment, before you do all this you will have to populate it, here is an easy query that does it
INSERT INTO stackoverflow_artist(name)
SELECT distinct temp_artist from stackoverflow_song
note that you should have a unique index on Artist.name
I need to read data for an external database (I cannot modify the table structure) with the two following tables:
Table1
Key (primary key)
Field1
Table2
Key (primary key)
Field2
Is it possible to combine these two tables into a single Django Model that looks like:
Model:
Key (primary key)
Field1
Field2
Additional information:
The keys of the entries in Table2 are a subset of the entries in Table1. So for any entry in Table2 there is a matching entry in Table1, but not viceversa
Work so far:
I have a one model for each table (1 and 2) and Table1 model has a "field2" #property that looks up the corresponding information in the Table2 model.
Also, I can do this at the sql level with the following query:
SELECT
table1.key,
table1.field1,
table2.field2,
FROM
table1
LEFT OUTER JOIN table2
ON table1.key=table2.key
ORDER BY table1.key ASC
===
Solution implementation details update after seeing some of the answers:
From Daniel Roseman's answer
I ended up with two models:
class Model1(models.Model):
key = models.CharField(max_length=100, primary_key=True)
field1 = models.TextField()
class Meta:
managed = False
db_table = 'Table1'
def field2_value(self):
try:
return self.field2.value
except Model2.DoesNotExist:
return None
class Model2(models.Model):
key = models.OneToOneField(Model1, primary_key=True, db_column='key',
related_name='field2')
field2 = models.TextField()
class Meta:
managed = False
db_table = 'Table2'
While it is not exactly what I had originally in mind when I asked this question, it meets my desired use case
Also, here are the corresponding admin classes:
class Model2Admin(admin.StackedInline):
model = Model2
#admin.register(Model1)
class Model1Admin(admin.ModelAdmin):
inlines = (Model2Admin,)
list_display = ('key', 'field1', 'field2_value')
# Loads the related property in one SQL call instead of one call per entry
list_select_related = ('field2',)
Thanks KaaN SARIKAYA for your great answer. I didn't know that I could create a view of the two tables. If I could modify the database structure I would opt for your option.
A better solution would be to keep these as two models, but have the primary key of the second be a OneToOneField to the first.
You cannot merge the model because they are 2 physical tables, django models are tied by the database table names so you couldn't do what you want. If you have the control for those external database tables and have the right to modify them, you should modify the schema first and merge the data. If you don't want to or you cannot modify anything, your current way is OK.
You can create a table_view on sql and make an abstract model on django . Suppose you have 2 pyhsical models and tables.
SQL CODE:
CREATE OR REPLACE VIEW combine_two_model_view AS
SELECT
mt1.name as mt1_name,
mt2.name as mt2_name,
mt1.pub_date as mt1_pub_date
.
.
.
.
FROM modeltable1 mt1
LEFT JOIN modeltable2 mt2 ON mt2.id = mt1.id
ALTER TABLE combine_two_model_view
OWNER TO dbuser;
we created sql view table. now, we will create an abstract model
in your models.py:
class YourModelsCombine(models.Model):
class Meta:
abstract = True
db_table = "combine_two_model_view"
mt1_name = models.CharField(max_length=100)
mt2_name = models.TextField(max_length=2000)
mt1_pub_date = models.DateTimeField("Publish Date", auto_now_add=True)
mt2_pub_date = models.DateField("Updated Date", auto_now=True)
mt1_integer = models.IntegerField(default=0)
#etc
attention: make sure your table view variable names and model variable names must be same. Finally;
in your admin.py:
admin.site.register(YourModelsCombine)
You will see combine of two tables on your django-admin
I hope this helps to you
I have a model in Django that looks like this (I've simplified the tables & skipped the irrelevant fields. I can't change the tables/relations):
Class Attachment(models.Model):
name = models.CharField()
Class Email(models.Model):
subject = models.CharField()
from = models.ForeignKey(User)
attach = models.ForeignKey(Attachment)
Class User(models.Model):
name = models.CharField()
In my view, I want to find all the Users that have sent this attachment. So, first I fetch all the emails that contain the this attachment
my_attachment = Attachment.objects.get(name='Picture1.jpg')
email_set = my_attachment.email_set.all()
What's an efficient way to fetch all the users that are listed in the from field of emails in email_set i.e. without looping through email_set.
This is the easy and efficient way:
users = ( User
.objects
.filter( email__attachment__name = 'Picture1.jpg' )
.distinct()
)
Remember to create needed indexes to your database tables.