I'm porting an Access application to Python/Django. There is a SchoolInfo table and a Campuses table with a foreign key pointing to the SchoolInfo table. In Access, I have a Campuses subform (continuous) embedded into the SchoolInfo form with match fields defined properly pointing to School_ID. So for School 1 I only show campuses for that school.
In Django, I added the SchoolInfo table to Admin and am trying to replicate the Access UI using TabularInline. Here are snippets of model and admin:
class Schoolinfo(models.Model):
school_id = models.AutoField(db_column='School_ID', primary_key=True) # Field name made lowercase.
schoolcode = models.CharField(db_column='SchoolCode', max_length=10) # Field name made lowercase.
schoolname = models.CharField(db_column='SchoolName', max_length=50, blank=True, null=True) # Field name made lowercase.
class Campuses(models.Model):
campus_id = models.AutoField(db_column='Campus_ID', primary_key=True) # Field name made lowercase.
campusno = models.IntegerField(db_column='CampusNo', default=1) # Field name made lowercase.
schoolcode = models.CharField(db_column='SchoolCode', max_length=10) # Field name made lowercase.
school = models.ForeignKey('Schoolinfo', models.DO_NOTHING, db_column='School_ID', unique=True) # Field name made lowercase.# school = models.IntegerField(db_column='School_ID') # Field name made lowercase.
campusname = models.CharField(db_column='CampusName', max_length=15, blank=True, null=True) # Field name made lowercase.
class CampusesInline(admin.TabularInline):
model = Campuses
admin.site.register(Campuses)
class SchoolInfoAdmin(admin.ModelAdmin):
fields = (
('schoolcode', 'schoolname'), ('contactname', 'contacttitle'), ('contactphone', 'contactemail'),
('clockhoursschool', 'schoolsendsawardletters'), ('tin_no', 'duns_no'), ('pell_id', 'ope_id'))
save_on_top = True
inlines = [CampusesInline,]
admin.site.register(Schoolinfo, SchoolInfoAdmin)
When I select a school for edit, it properly shows the school fields, then the TabularInline for campuses, but here is the problem:
There are multiple schools in the database, each having 1 or more campuses. The TabularInline properly shows the campus or campuses for the active school, but it also has several blank records (which I assume are related to the campus records for other schools).
If I add "unique=True" to the foreign key (as shown in the model above), the blank records disappear and all looks good. Unfortunately, I also loos the link at the bottom of the inline to add a new campus.
I'm VERY new to Python/Django, so can't figure out how to fix this. I suppose my choices are some type of filtering in the inline (and take out the unique=True from the model), or leave that in and figure out how to restore the "add" link to the inline.
Are there any suggestions on how to fix this?
Thanks...
but it also has several blank records (which I assume are related to
the campus records for other schools).
Those "blank" records are for adding new campuses to the school. You can use the "extra" attribute on your CampusInline to control the number of blank lines.
If I add "unique=True" to the foreign key (as shown in the model
above), the blank records disappear and all looks good. Unfortunately,
I also loos the link at the bottom of the inline to add a new campus.
If you add "unique" to the "school" field, that would mean that a school could only appear once in the campus table. Which doesn't (usually) make much sense for a foreign key.
Related
The following is in my models.py:
class SensorType(models.Model):
hardware_type = models.CharField(max_length=100)
is_static = models.BooleanField(default=False)
# Some other fields
class Sensor(models.Model):
device_id = models.CharField(max_length=100, primary_key=True)
sensor_type = models.ForeignKey(SensorType, on_delete=models.PROTECT)
# Some other fields
class Asset(models.Model):
name = models.CharField(max_length=100)
sensor_type = models.ForeignKey(SensorType, on_delete=models.PROTECT) # I need to use this field to filter below
sensor = models.ForeignKey(Sensor, on_delete=models.PROTECT, limit_choices_to={'sensor_type': WHAT DO I PUT HERE?},)
# Some other fields
I need to limit the choices in the sensor field of asset so that only sensors with the sensor_type set in the field immediately above, show up.
The reasoning behind this is that there will eventually be many sensors and it would be very useful to filter this. Initially I only need this to work from the admin page but this will eventually extend when I make my Create and Update Views.
Is this even possible? I'm essentially trying to access attributes before the object has actually been created.
After reading several other questions such as this one I have also looked into ModelChoiceField but the same issue exists of trying to access the form data before it has been submitted.
I'm very open to changing the model structure if that is what is required.
I have a model with double columns as primary key. I do a filter on it and get the records I want, change a field and save it. As I know save will update the record and does not create a new instance of the model in db. so It should be all okay but I'm stuck with an integrityError Duplicate entry '10-2' for key 'PRIMARY' when I try to save the record
Here is the code snippet:
analysis = AnalysisResult.objects.filter(request=req.request_id)
for anal in analysis:
anal.analysisresult_path = some_string
anal.save() #this line is where the error occurs
And here is my model:
class AnalysisResult(models.Model):
analysisresult_path = models.CharField(db_column='analysisResult_path', max_length=255, blank=True,
null=True) # Field name made lowercase.
detectionresult_path = models.CharField(db_column='detectionResult_path', max_length=255, blank=True,
null=True) # Field name made lowercase.
targetcode = models.ForeignKey('TagetCode', models.DO_NOTHING,
db_column='targetCode_id') # Field name made lowercase.
request = models.ForeignKey('Request', models.DO_NOTHING, primary_key=True)
class Meta:
managed = False
db_table = 'analysis_result'
unique_together = (('request', 'targetcode'),)
Ah, yes, welcome to one of django's strongest opinions: all tables/models should have a single primary key field that can be used for updates. If you don't have this, you must write raw SQL for the update since save will assume that there is an implicit primary key field called id to use in the where clause.
I am using Djano to develop a simple web app to display and manage database data. I hooked up a MySQL db and used inspectdb to auto generate a model based on the database tables and this is what I got back, which looks good.
# This is an auto-generated Django model module.
# You'll have to do the following manually to clean this up:
# * Rearrange models' order
# * Make sure each model has one field with primary_key=True
# * Make sure each ForeignKey has `on_delete` set to the desired behavior.
# * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table
# Feel free to rename the models, but don't rename db_table values or field names.
from __future__ import unicode_literals
from django.core.exceptions import MultipleObjectsReturned
from django.db import models
class Booking(models.Model):
class Meta:
managed = False
db_table = 'Booking'
unique_together = (('hotelno', 'guestno', 'datefrom'),)
hotelno = models.OneToOneField('Hotel', models.DO_NOTHING, db_column='hotelNo', primary_key=True) # Field name made lowercase.
guestno = models.IntegerField(db_column='guestNo') # Field name made lowercase.
datefrom = models.DateTimeField(db_column='dateFrom') # Field name made lowercase.
dateto = models.DateTimeField(db_column='dateTo', blank=True, null=True) # Field name made lowercase.
roomno = models.OneToOneField('Room', models.DO_NOTHING, db_column='roomNo') # Field name made lowercase.
list_display =
#def __str__(self):
# return ("".join(hotelno) + "".join(guestno) + "".join(datefrom))
class Guest(models.Model):
guestno = models.AutoField(db_column='guestNo', primary_key=True) # Field name made lowercase.
guestname = models.CharField(db_column='guestName', max_length=255) # Field name made lowercase.
guestaddress = models.CharField(db_column='guestAddress', max_length=255, blank=True, null=True) # Field name made lowercase.
class Meta:
managed = False
db_table = 'Guest'
class Hotel(models.Model):
hotelno = models.AutoField(db_column='hotelNo', primary_key=True) # Field name made lowercase.
hotelname = models.CharField(db_column='hotelName', max_length=255, blank=True, null=True) # Field name made lowercase.
city = models.CharField(max_length=255, blank=True, null=True)
class Meta:
managed = False
db_table = 'Hotel'
class Room(models.Model):
roomno = models.IntegerField(db_column='roomNo', primary_key=True) # Field name made lowercase.
hotelno = models.ForeignKey(Hotel, models.DO_NOTHING, db_column='hotelNo') # Field name made lowercase.
type = models.CharField(max_length=255, blank=True, null=True)
price = models.IntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'Room'
unique_together = (('roomno', 'hotelno'),)
In the admin.py file for this app I included the models like so, so that I could at least see the data up there.
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.contrib import admin
# Register your models here.
from .models import Hotel, Room, Guest, Booking
admin.site.register(Hotel)
admin.site.register(Room)
admin.site.register(Guest)
admin.site.register(Booking)
When I access the default Django admin page, I'll see the tables registered on admin page.
I click on Bookings and see the multiple records, without names (for other reasons), but if I click on one of them I get the MultipleObjectsReturned Error
I've read everything I could find, and the closest thing to a reason I could find for why this is happening has to do with there being composite keys in some of the models. But, again, I don't know if that's the actual reason, I could also be missing something? I don't know.
I guess the reason is that data of Booking table not consistent with your model declaration. Django's admin detail view retrieves model by primary key.
You marked hotelno as PK:
hotelno = models.OneToOneField('Hotel', models.DO_NOTHING, db_column='hotelNo', primary_key=True)
Since some data already exists in Booking table, you have to make sure that hotelno values (hotelNo column) are unique or you will get MultipleObjectsReturned exception for non-unique pk values. Also make sure you've read this part of the documentation https://docs.djangoproject.com/en/1.11/ref/models/options/#managed
It looks like your booking table does not have a primary key and inspectdb guessed wrong when affecting it to the hotelno column of your booking table.
When the admin try to get the record by it's id it get multiple result since different booking can reference the same hotel multiple time.
What I would do on the booking model :
change hotelno and roomno to ForeignKey
remove the primary on hotelno
The new problem is now you have a model with no primary key. Django does not allow that. If you can alter the MySQL table add a primary key column to it and alter the booking model accordingly. If you can't alter the table I see no easy way to make it works.
So I currently have an "Account" Model and "Account Comments" Model -- keep in mind, that I can't really control the scheme of the database, and I'm writing a wrapper around existing DB.
Under the AccountComments Model, there is a field called "Data". It is where the AccountComments actually are and I'm trying to basically create a foreign key on the Account model without actually having to redesign and add a "AccountComments" field on Account that holds the AccountID.
class Account(models.Model):
AccountID = models.AutoField(editable=False, db_column='AccountID', verbose_name='ID',
primary_key=True)
acctno = models.IntegerField(editable=True, unique=True, db_column='ACCTNO', verbose_name='Acct #', blank=True,
null=True) # Field name made lowercase.
accountComments = models.ForeignKey('accountComments',to_field='accountID',db_column='Data')
def __str__(self):
return str(self.acctno)
class Meta:
managed = False
db_table = 'Account'
class accountComments(models.Model):
accountCommentID = models.AutoField(db_column='AccountCommentID', primary_key=True) # Field name made lowercase.
accountID = models.IntegerField(db_column='AccountID') # Field name made lowercase.
EntryUserID = models.IntegerField(db_column='EntryUserID') # Field name made lowercase.
EntryStamp = models.DateTimeField(db_column='EntryStamp', ) # Field name made lowercase.
Data = models.TextField(db_column='Data') # Field name made lowercase.
guid = models.CharField(db_column='guid', max_length=255) # Field name made lowercase.
class Meta:
managed = False
db_table = 'AccountComment'
ultimately, want accountComments on the Account Model to use 'AccountID' to do a lookup onto accountComments.accountID and then provide back 'Data'.
I know that I can use
def accountComments(self):
return str(accountComment.objects.get(accountID = self.AccountID).Data)
but I want it to work with Django Admin, so I need it to be an integrated part of the model.
Thanks if anyone can point me in the right direction.
You're trying to do too much with a foreign key. Following a foreign key in Django should return the model instance, not a particular field from the model instance.
The db_column should be the column in the Account table that stores the id, e.g.
accountComments = models.ForeignKey('accountComments',to_field='accountID',db_column='AccountID')
Then, to get the Data for a particular account instance, you would do:
account.accountComments.Data
I am trying to make my ID in class Animal unique, so that it automatically increments without a user having to add data into the filed (so the filed is hidden), but I can't figure out how to do it.
Right now I have this in my models.py:
class Animal(models.Model):
id = models.CharField(max_length=100, blank=True, unique=True, default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=200)
Which in my mind will have to make id unique, but currently it doesn't do anything.
Here is my serialisers.py for reference:
class AnimalSerialiser(serializers.HyperlinkedModelSerializer):
doctor = DoctorSerealiser(read_only=True)
class Meta:
model = Animal
fields = ('id' , 'name' , 'gender' , 'breed' , 'adoption' , 'vaccines', 'doctor')
As other people have mentioned in the comments, you're defining your model wrong. Django adds an auto-incrementing field by default. Take a look at the docs for more information - https://docs.djangoproject.com/en/1.9/topics/db/models/#automatic-primary-key-fields.
You really just need:
class Animal(models.Model):
name = models.CharField(max_length=200)
and then you can still access that field like animal.id.
And UUID's are totally different than an auto-incrementing ID. See the wiki - they're basically just strings that are nearly impossible to generate duplicates of.