Django: Foreign key from parent to child table - python

I want to have a foreign key link from Parent table to child table ; for a site which is mainly backend powered. Currently only admin part of site is active as that is the only part required to get our information team working.
App structure:
attraction:
|
|--- model
| |
| | --- _ _init__.py
| | --- imagemetadata.py
|
| --- models.py
| --- admin.py
(imagemetadata is under directory model)
file imagemetadata.py
from attraction.models import Attraction
class ImageMetadata(models.Model):
image = models.ImageField(upload_to='', blank=True, null=True)
upload_time = models.DateTimeField(null=True)
attraction = models.ForeignKey(Attraction)
file models.py
from django.db import models
class Attraction(models.Model):
name = models.CharField(max_length=100, null=False)
description = models.CharField(max_length=500, null=True)
url = models.URLField(max_length=200, null=True)
file admin.py
class ImageMetadataInline(admin.TabularInline):
model = ImageMetadata
extra = 2
class AttractionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['name', 'description', 'url']}),
]
inlines = [ImageMetadataInline, PriceInline]
admin.site.register(Attraction, AttractionAdmin)
The problem i am facing is i cant get a foreign key from Attraction to Imagemetadata. If I try to make a two way foreign key import error occurs. ( Cyclic imports not allowed in Python).
And because of the structure of admin , there cant be a foreign key just from Attraction to ImageMetadata, as Django doesnt allow that.
Is there a way to make the foreign key point from Attraction to ImageMetadata, without changing the structure of admin ?

You can define the class Meta as following:
class ImageMetadata(models.Model):
# ... your fields
class Meta:
app_label = 'attraction'
Make a sub-directory models in your app directory. Split your models into different files. Then edit the __init__.py:
from model_file1 import *
from model_fiel2 import *
# and so on
Now you can make:
from attraction.models import whatever
I split in this way my tests. It should work for the models too.
However I would suggest you first to rethink your design. In Python is absolutely all right to have many classes in one file. Python is neither PHP nor Java. 200 LOC per file is nothing. I would say it is absolutely okay if your models.py are up to 2k LOC. Just my personal opinion. Maybe some Python gurus here can correct me.
Before you split your models.py consider if you can maybe split your project in smaller apps. Maybe ImageMetadata should belong to another app. You can't achieve granularity by splitting the models.py into many files. Just because you have many smaller files instead of one big file doesn't mean the project logic is split. You should break down your logic into few self-containing apps.

Related

Two models pointing to one database table in Django 2.1

I'm working on a Django project made by a former employee of the company (so I'm refactoring a whole project made by somebody else that didn't follow Django Best Practices), that have two models on different apps using the same tables on database. The City and State tables are used in both apps.
I want to know which is the best way to apply DRY concepts and use only one model for the two apps access these tables.
The two apps are on the project folder and each one has his own models.py with the following code for city/state:
from django.db import models
from django.contrib.auth.models import User,Group
from django.db.models.signals import post_save
from django.dispatch import receiver
class state(models.Model):
class Meta:
db_table = '"db_property"."state"'
created_at = models.DateTimeField(db_column='created_at')
updated_at = models.DateTimeField(db_column='updated_at')
name = models.CharField(db_column='name',max_length=50)
class city(models.Model):
class Meta:
db_table = '"db_property"."city"'
created_at = models.DateTimeField(db_column='created_at')
updated_at = models.DateTimeField(db_column='updated_at')
name = models.CharField(db_column='name',max_length=50)
state = models.ForeignKey(state,on_delete=models.CASCADE)
Am I missing something?
Put city and state in one or other app, or even in their own citystate app, and import them from the place they are defined. In an app called foo:
from citystate.models import city, state
In passing, Django models are classes, and as such one would normally start them with a capital letter: City and State. Honoring conventions like this matter: you may not be confused (yet) but you will confuse the heck out of anybody else reading this code, who will think that these things being imported are functions not classes!
An app is not required to have any views, urls, etc. It can be just a place to put common models and their migrations, and maybe some admin classes.

Build a learning platform - link exercise to its lesson - onetoone field or foreign key?

I'm trying to build a small project - an e-learning project. I'm trying to bind some exercises to its lesson id.
I've read the django docs and I don't know if I should use a OneToOne field or a Foreign Key.
I've tried the idea with the foreign key, as I feel like this is the right answer.
lessons - models.py (Lectie = lesson)
from django.db import models
# Create your models here.
class Lectie(models.Model):
YTLink = models.CharField(max_length = 100)
PDFLink = models.CharField(max_length = 100)
exercises - models.py (intrebare = question, variante = options, variantaCorecta = the right answer)
from django.db import models
from django.contrib.postgres.fields import ArrayField
from lectii.models import Lectie
# Create your models here.
class Exercises(models.Model):
idLectie = models.ForeignKey(Lectie, on_delete=models.DO_NOTHING)
intrebare = models.CharField(max_length = 300)
variante = ArrayField(models.CharField(max_length=300), null=True)
variantaCorecta = models.IntegerField()
def __str__(self):
return self.intrebare
I'm getting this error:
You are trying to add a non-nullable field 'idLectie' to exercises without a default; we can't do that (the database needs something to populate existing rows).
I will only add these questions from the back-end, they will not be added by the user, and I get this answer. Django doesn't know what ID to bind the exercise to.
So how should my model look so I can bind the exercise to its lesson? Is it okay this way and just adding a blank=True, and changing the ID after that? Or should I switch to a one-to-one relationship? Thanks.
Thanks.
If a lesson has multiple lessons, then a ForeignKey is appropriate. A OneToOne field is just a ForeignKey with a unique=True constraint, meaning only one relationship between the two objects is allowed.
As for your error, you have two options:
1) Set a default=... attribute, which wouldn't make sense in this case since it doesn't seem there should be a "default" exercise for each lesson.
2) Set the ForeignKey to blank=True, null=True, then set each Lesson manually. You can always remove blank=True, null=True and then migrate again.
You need to provide a default value:
DEFAULT_ID = 1
idLectie = models.ForeignKey(Lectie, on_delete=models.DO_NOTHING, default=DEFAULT_ID)
Also make sure that there is a row in the table of Other.

Having a django model which can belong to either of two other models (foreign key relation)

I'm trying to emulate a file browser application using django. My basic models are where a user has a project and the project has files and other subdirectories which also can have files.
this is my models.py file
class CaseFolder(models.Model):
name = models.CharField(max_length = 255)
class SubFolders(models.Model):
name = models.CharField(max_length = 255)
case = models.ForeignKey(CaseFolder)
class Documents(models.Model):
file = models.FileField(upload_to=set_upload_path)
belongs_to = models.ForeignKey(SubFolders)
As of now , I’m creating a "MAIN" folder which basically is the root folder which has the other subdirectories. The Main folder can also have files which do no belong to the subdirectories.
It would be preferable if I can eliminate the need for a 'main' folder by having the Documents model refer to Root folder if they dont want to belong to a subdirectory. THe only way i see around this is the below. But would like to know if theres a better way
class Documents(models.Model):
file = models.FileField(upload_to=set_upload_path)
belongs_to = models.ForeignKey(SubFolders,Null = True)
belongs_to_root = models.BooleanField(deafult=False)
Forget about the SubFolders model.
You can simulate theese structures with a self-referenced relation in the CaseFolder model, checkout:
class CaseFolder(models.Model):
name = models.CharField()
parent = models.ForeignKey('self', blank=True, related_name='subfolders')
class Document(models.Model):
file = models.FileField(upload_to=set_upload_path)
belongs_to = models.ForeignKey(CaseFolder)
To know if a Document belongs to the root, just use document.belongs_to.parent is None.
To provide an alternative. You can use django-mptt.
pip install django-mptt
In your settings.py add mptt into the REQUIRED_APPS.
What this allows you to do is the following in your models:
from mptt.models import MPTTModel, TreeForeignKey
class CaseFolder(MPTTModel):
name = models.CharField()
parent = models.TreeForeignKey('self', blank=True, related_name='subfolder', db_index=True)
class MPTTMeta:
order_insertion_by = ['name']
What this does on the database side is index the table to allow referencing between parent and child. This will (in theory) make your queries to the database easier and speed up your application as it grows. It also simplifies how the data is queried.

where to put relation models in django

First the simplified scenario:
from django.db import models
class Product(models.Model):
name = models.TextField()
description = models.TextField()
class Merchant(models.Model):
name = models.TextField()
class MerchantProductMapping(models.Model):
merchant = models.ForeignKey(Merchant)
product = models.ForeignKey(Product)
price = models.IntegerField()
inventory_limit = models.IntegerField()
I have another model for the relation (MerchantProductMapping) because the relation has attributes of its own. Now the requirements of the Merchant and the Product model have grown to a point where they demand separate apps of their own. The merchant app's models.py is where the Merchant model will live and the product app's models.py is where the Product model will live.
What I need help with is the relation model MerchantProductMapping. It is needed by both apps, where should I put it ? I've been reading up on mixins and wondering if they could help me somehow.
EDIT: I should add that the app was rendered server side earlier. Now it will be done using angular client - REST api approach. And django rest framework will be used on top of django.
Create "common" app for such purposes ... you can put there decorators, templatetags, base forms, base models, login|logout redirect views and urls, ajax views, base filters, base tables ... etc
Note: create "apps" python package dir (dir with __init__.py inside it) and (refactor) move all your apps there.
EDIT:
Another way - create "models" python package dir and split your models.py to logically separated files inside package

Django admin search and edit foreign fields

I've got a two part question regarding Django Admin.
Firstly, I've got a Django model Classified that has a foreign key field from another table Address. On setting data, I've got no issues with any of the fields and all fields get saved correctly.
However, if I want to edit the foreign field in the entry in Classified, it doesn't display the old/existing data in the fields. Instead it shows empty fields in the popup that opens.
How do I get the fields to display the existing data on clicking the + so that I can edit the correct information?
Secondly, I'm sure I've seen search fields in Django Admin. Am I mistaken? Is there a way for me to implement search in the admin panel? I have over 2 million records which need to be updated deleted from time to time. It's very cumbersome to manually go through all the pages in the admin and delete or edit those.
Adding Model Code:
Classified
class Classified(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=256)
contact_person = models.CharField(max_length=300, blank=True)
email = models.CharField(max_length=100, blank=True)
address = models.ForeignKey(Address)
subcategory = models.ForeignKey(Subcategory)
Address
class Address(models.Model):
id = models.AutoField(primary_key=True)
build_add = models.CharField(max_length=255)
street_add = models.CharField(max_length=255)
area = models.CharField(max_length=255)
city = models.ForeignKey(Cities)
The + means just that - add a new instance of the related object and relate the object you're editing to that. Because you're adding a new object it will be blank to start. If you want to be able to edit existing related objects from another object's admin you need to use inlines.
In your app's admin.py have something like:
from django.contrib import admin
from yourapp.models import Address, Classified
class AddressInline(admin.TabularInline):
model = Address
class ClassifiedAdmin(admin.ModelAdmin):
inlines = [AddressInline,]
admin.site.register(Classified, ClassifiedAdmin)
Adding search from there is really easy.
...
class ClassifiedAdmin(admin.ModelAdmin):
inlines = [AddressInline,]
search_fields = [
'field_you_want_to_search',
'another_field',
'address__field_on_relation',
]
...
Note the double underscore in that last one. That means you can search based on values in related objects' fields.
EDIT: This answer is right in that your foreignkey relationship is the wrong way round to do it this way - with the models shown in your question Classified would be the inline and Address the primary model.

Categories

Resources