Converting IntegerField to ForeignKey from recovered database in Django - python

I'm working with a MySQL database that I connected to my django project (using python manage.py inspectdb > app/models.py) but it's not reading the relationships correctly. The ForeignKeys are IntegerFields so I cannot use the handy Django ORM lookup. Here's an example:
class ChileStudents(models.Model):
""" simplified version of the model """
otec = models.IntegerField(blank=True, null=True) # Here's the issue
# More stuff goes here
updated_at = models.DateTimeField(blank=True, null=True)
class Meta:
managed = True
db_table = 'chile_students'
class Otecs(models.Model):
""" Also a simplified version """
name = models.CharField(max_length=45, blank=True, null=True)
updated_at = models.DateTimeField(blank=True, null=True)
class Meta:
managed = True
db_table = 'otecs'
As shown in the example above, the IntegerField points to the OTEC id, this is a simple one-to-many relationship. I tried converting the field into a ForeignKey like this:
otec = models.ForeignKey('Otecs', on_delete=models.SET_NULL,blank=True, null=True, db_column="otec_id")
But when migrating it just sets the column otec_id to NULL.
Is there any way I can "convert" the field into a ForeingKey?

You can specify that the name of the database column is otec with db_column='otec':
class ChileStudents(models.Model):
otec = models.ForeignKey(db_column='otec', blank=True, null=True)
# More stuff goes here
updated_at = models.DateTimeField(blank=True, null=True)
class Meta:
managed = False
db_table = 'chile_students'
But since the table already exists, you can not make it managed = True, since then Django will try to create the table at the database side, create/remove columns, etc. Since here the table already exists, you can not let Django handle that, since then it will aim to create a table that already exists.

Related

Usertype models in Django

I'm being confused on where table how I insert my usertypes, I'm just a beginner on Django but surely Im been reading documentation in Django but I can't understand this one , The case is when I register new user there's must be choice what usertype should specify with this user either an admin or etc. but the problem is I think there is no relationship table from authuser even I create another table.slight similar to this problem resources link. For now I'm been thinking to create custom usertype field in authuser table, but when migrate it didn't show updated fields and also like this issue some people or user didn't touch or add any field in authuser table sample it is possible to insert usertype in auth_permission or other default table? Im just really confused of where table I can add my usertype that have a relationship to authuser. Is there any know or explain about this, thanks
Models
class AuthUser(models.Model):
password = models.CharField(max_length=128)
last_login = models.DateTimeField(blank=True, null=True)
is_superuser = models.IntegerField()
username = models.CharField(unique=True, max_length=150)
first_name = models.CharField(max_length=150)
last_name = models.CharField(max_length=150)
email = models.CharField(max_length=254)
is_staff = models.IntegerField()
is_active = models.IntegerField()
date_joined = models.DateTimeField()
usertype_id = usertype = models.OneToOneField(usertypes,on_delete=models.CASCADE,primary_key=True)
() //this is what i want to add
class Meta:
managed = False
db_table = 'auth_user'
class usertypes(models.Model):
usertype = models.CharField(max_length=264)
description = models.CharField(max_length=264)
status = models.CharField(max_length=264)
There are multiple ways how to do it. The way I recommned is to extend the existing user model.
from django.contrib.auth.models import User
class UserType(models.Model):
user_type = models.CharField(max_length=264)
description = models.CharField(max_length=264)
status = models.CharField(max_length=264)
class AppUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
type = models.ForeignKey(UserType, on_delete=models.CASCADE)
This will create 2 extra tables.
user_type where you will have UserTypes that you probably want to fill and
app_user where will be stored reference on django user record and its type
However, I think you should have a good reason why you do this. Django allows you to group users into the user groups, what should be exactly what you want.

Using the __str__ method in Django to turn an object into a string - not working

I have the following code in models.py, note the function at the bottom that seeks to change the teacher_object in the admin panel to the username of the teacher instead.
class Teachers(models.Model):
email = models.CharField(max_length=50)
school_pin = models.CharField(max_length=11)
username = models.CharField(max_length=50)
pass_field = models.CharField(db_column='pass', max_length=100) # Field renamed because it was a Python reserved word.
fname = models.CharField(max_length=50, blank=True, null=True)
lname = models.CharField(max_length=50, blank=True, null=True)
oauth_provider = models.CharField(max_length=50, blank=True, null=True)
oauth_uid = models.CharField(max_length=100, blank=True, null=True)
verified = models.IntegerField(blank=True, null=True)
verificationcode = models.CharField(db_column='verificationCode', max_length=100, blank=True, null=True) # Field name made lowercase.
school_name = models.CharField(max_length=255, blank=True, null=True)
designation = models.CharField(max_length=255, blank=True, null=True)
date = models.DateField(blank=True, null=True)
membershipid = models.IntegerField(blank=True, null=True)
def __str__(self):
return self.username
class Meta:
managed = False
db_table = 'teachers'
The admin panel still shows the teachers listed as Teachers object (1275) (and so on).
I have tried the following:
1. Running the makemigrations and migrate commands
2. Logging in and out of the admin panel
3. Refreshing and closing down CMD - restarting
None of the above has worked. There are no errors on running the server.
Python version: 3.7 Latest Django installation
Considerations:
1. I was using a legacy mysql database so reverse created the models. Could this be affecting anything?
2 I wonder if the class Meta (at the bottom) has any bearing on the error.
I also tried the following, but still no result:
def __str__(self):
return '%s %s' %(self.username,self.email)
For reference, and in case this is relevant here is my admin.py
from django.contrib import admin
# Register your models here.
from django.contrib import admin
from .models import Teachers
from .models import TeacherPins
admin.site.register(Teachers)
admin.site.register(TeacherPins)
Finally, as this is a reverse-generated model (from a legacy mysql) Django provided this advice at the start of the models.py file. Again, could this (not being done, e.g not having a primary key etc) have a bearing on the str method not working?
# 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.
Update:
I did try rearranging the order (of the classes) - would this make a difference? But still the object remained an object
I tried adding a primary key to both classes - this didn't work either
student_id = models.IntegerField(blank=True, null=True, primary_key=True)

Django MultipleObjectsReturned while using a legacy database

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.

Django ManyToMany Field with an already existing table

What I'm trying to achieve is, having model Person that is created and managed by Django have a ManyToMany field with model Property that was "created" using inspectdb and already exists in the database.
(Property contains Geographical data and cannot be managed or changed by Django)
When trying to migrate, it raises :
ValueError: Related model 'cadastroapp.Property' cannot be resolved
Full stack here
Worth nothing that I removed from the migration file the step to create model Property, since it already exists and AFAIK there's no way to tell Django this in the model Class
models.py (simplified) :
class Person(models.Model):
objectid = models.AutoField(primary_key=True)
properties = models.ManyToManyField(
'Property',
through = 'Person_Property',
)
class Meta:
db_table = 'django_person'
class Person_Property(models.Model):
cod_person = models.ForeignKey('Person', on_delete=models.CASCADE)
cod_property = models.ForeignKey('Property', on_delete=models.CASCADE)
class Meta:
db_table = 'django_person_property'
class Property(models.Model):
objectid = models.BigIntegerField(unique=True, primary_key=True)
created_user = models.CharField(max_length=765, blank=True, null=True)
created_date = models.DateTimeField(blank=True, null=True)
last_edited_user = models.CharField(max_length=765, blank=True, null=True)
last_edited_date = models.DateTimeField(blank=True, null=True)
shape = models.TextField(blank=True, null=True) # This field type is a guess. - ESRI Shape
class Meta:
managed = False
db_table = '"GEO"."PROPERTY"'
There are a couple errors in your models.py file.
When defining a Foreignkey or ManytoMany field, you don't want the model name to be in quotes.
Please change:
class Person(models.Model):
properties = models.ManyToManyField(
'Property',
through = 'Person_Property',
)
and
class Person_Property(models.Model):
cod_person = models.ForeignKey('Person', on_delete=models.CASCADE)
cod_property = models.ForeignKey('Property', on_delete=models.CASCADE)
to:
class Person(models.Model):
properties = models.ManyToManyField(
Property,
through = 'Person_Property',
)
and
class Person_Property(models.Model):
cod_person = models.ForeignKey(Person, on_delete=models.CASCADE)
cod_property = models.ForeignKey(Property, on_delete=models.CASCADE)
then delete your migration file cadastroapp.0006_auto_20161122_1533.
then run makemigrations and migrate again.
This may still not migrate without errors, but it will get us on the right track.
I think that you want to put the model name in quotes. In case you leave it without quotes you have to ensure that the model is defined before the ManyToMany field has been defined. So you will need to have first class Property and then class Person in your file. When you put model name as "Property" then you do not need to care about order of class definitions.

Django reference a Model by foreign key or a different field

I am using Django REST Framework. I have two models, Sites and Statuses.
class Sites(models.Model):
site_id = models.AutoField(primary_key=True)
status = models.ForeignKey(Statuses, models.DO_NOTHING, blank=True, null=True)
class Statuses(models.Model):
status_id = models.AutoField(primary_key=True)
description = models.CharField(max_length=255, blank=True, null=True, unique=True)
class Meta:
managed = True
db_table = 'Statuses'
I would like to be able to perform a GET on sites, and have the Statuses.description field returned (instead of Statuses.status_id). Also, I would like it so that either status_id or description may be used interchangeably in a POST to create a new site. Where does this type of functionality belong (serializer, models, etc...)?
I know I can accomplish the first part of my question by adding a property to the Sites model and then referencing this field in the Sites serializer.
#property
def status(self):
return self.row_status.description
However I thought the convention of a Model is that it should be a 1:1 representation of the database table. Is there a better way to do this?
This fits well in the serializer, like this:
class SitesSerializer(serializers.ModelSerializer):
description = serializers.CharField(source='status.description')
class Meta:
model = Sites
fields = ('site_id', 'description')
(But the status field should probably not have null=True set.)

Categories