Django: How do I get a nested foreignkey to continuously display? - python

For academic purposes, I am creating software that can manage a company's clients, project, and staff members. I figured out that by referencing a foreignkey in a separate model, you can get two models to display next to each other and be related. The problem is, is that it only displays one item for each model.
For example: You have a project your company is working on. In the admin section, it displays the project name and a single staff member. However, it should be displaying 2+ staff members.
Here's the file layout (I tried creating a separate app for different fields clients being the primary and projects the secondary):
clients.admin:
from django.contrib import admin
from django.db import models
from clients.models import Status, Project, Staff_Member, Client
class StatusInline(admin.TabularInline):
model = Status
extra = 0
class Staff_MemberInline(admin.TabularInline):
model = Staff_Member
extra = 5
project = models.ForeignKey(Project)
class ProjectInline(admin.TabularInline):
model = Project
extra = 1
class ClientAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['name']}),
]
inlines = [ProjectInline, StatusInline]
search_fields = ['name']
admin.site.register(Client, ClientAdmin)
#admin.site.register(Project)
admin.site.register(Staff_Member)
next file
clients.models:
from django.db import models
import datetime
from django.utils import timezone
# Create your models here.
class Client(models.Model):
name = models.CharField(max_length=200)
def __unicode__(self):
return self.name
#def was_published_recently(self):
# return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
#was_published_recently.admin_order_filed = 'pub_date'
#was_published_recently.boolean = True
#was_published_recently.short_description = 'Published recently?'
class Status(models.Model):
client = models.ForeignKey(Client)
#staff_member = models.ForeignKey(Staff_Member)
#project = models.ForeignKey(Project)
status_text = models.CharField(max_length=200)
def stat(self):
return self.status_text
def __unicode__(self):
return self.status_text
class Staff_Member(models.Model):
#status = models.ForeignKey(Status)
#project = models.ForeignKey(Project)
#client = models.ForeignKey(Client)
staff_member_text = models.CharField(max_length=200)
def __unicode__(self):
return self.staff_member_text
def mem(self):
return self.staff_member_text
class Project(models.Model):
client = models.ForeignKey(Client)
project_text = models.CharField(max_length=200)
staff_member = models.ForeignKey(Staff_Member)
#status = models.ForeignKey(Status)
def __unicode__(self):
full_name = self.client.name +'\'s '+ self.project_text
return full_name
def project_name(self):
return self.project_text
name = client.name
next file
projects.admin:
from django.contrib import admin
from django.db import models
from clients.models import Project, Status, Client
from clients.admin import StatusInline, Staff_MemberInline
class ClientInline(admin.TabularInline):
model = Client
extra = 1
#class ProjectAdmin(admin.ModelAdmin):
# inlines = [Staff_MemberInline, ClientInline]
#admin.site.unregister(Project)
admin.site.register(Project)#, ProjectAdmin)
next file
projects.models
from django.db import models
import datetime
from django.utils import timezone
from django.db import models
from clients.models import *
# Create your models here.
I'm pretty new to stackoverflow, so if there's a way I'm missing to make this more eye-friendly let me know.
Thanks!

Change extra = 1 to a higher number. More info in the docs.

Related

Calculate a field django and save it in the database

I am just starting with Django and web development , so please be nice with me. i am trying to build an app that generates a new number to reports created by our inspectors (our company is an inspection company). each inspector chooses the type of reports, the inspection date and the client and the app saves the data and creates a report number . the report number is generated depending on the type of the report and the date. for example if i have 3 reports of type "visual inspection" done in 2022, the new "visual inspection" report will have the number 4/2022.
here is the code I used to attempt this but unfortunately it is not working:
Views.py
from modulefinder import ReplacePackage
from django.shortcuts import render
from report.models import Report
from django.http import HttpResponse
from django.db.models.aggregates import Count
from django.db import transaction
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .serializers import ReportSerializer
#api_view(['GET','POST'])
def Report_add_view(request):
if request.method =='GET':
queryset = Report.objects.select_related('type').all()
serializer = ReportSerializer(queryset,many=True)
return Response(serializer.data,status=status.HTTP_200_OK)
elif request.method =='POST':
serializer = ReportSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data,status=status.HTTP_201_CREATED)
# Create your views here.
models.py
from django.db import models
# Create your models here.
class Client(models.Model):
name = models.CharField(max_length = 255)
code = models.PositiveIntegerField()
class ReportType(models.Model):
report_type = models.CharField(max_length = 255)
report_code = models.CharField(max_length = 255)
report_rev = models.CharField(max_length = 20)
def __str__(self):
return self.report_type
class Inspector(models.Model):
first_name = models.CharField(max_length = 255)
last_name = models.CharField(max_length = 255)
full_name = models.CharField(max_length = 255)
class Report (models.Model):
number = models.CharField(max_length=100)
type = models.CharField(max_length = 255)
inspection_date = models.DateField()
date_created = models.DateTimeField(auto_now_add=True)
inspector = models.ForeignKey(Inspector,on_delete=models.PROTECT)
client = models.ForeignKey(Client,on_delete=models.PROTECT)
type = models.ForeignKey(ReportType,on_delete=models.PROTECT)
reportlink = models.TextField()
serializers.py
from rest_framework import serializers
from .models import *
from django.db.models.aggregates import Count
from django.db.models import F
class ReportSerializer(serializers.ModelSerializer):
class Meta:
fields = ['number','type','inspection_date','inspector','client','reportlink','date_created']
model = Report
number = serializers.SerializerMethodField(method_name='report_number')
def report_number(self, report:Report):
print(report.type_id)
num1 = Report.objects.filter(type_id = report.type_id).all().aggregate(num=Count('id'))['num']+1
year = str(report.inspection_date)[:4]
print(year)
print(num1)
return str(num1) + "/"+ year
class InspectorSerializer(serializers.ModelSerializer):
class Meta:
fields = ['first_name','last_name','full_name']
model = Inspector
class ReportTypeSerializer(serializers.ModelSerializer):
class Meta:
fields = ['report_type','report_code','report_rev']
model = Report
screenshot of api result:
database screenshot:
As stated in the DRF doc: https://www.django-rest-framework.org/api-guide/fields/#serializermethodfield
SerializerMethodField is a read_only field.
To achieve what you're trying to do, i would override the "create" method of the serializer. It takes a dict of validated_data as parameter (generate thanks to the is_valid call you're doing).
In this method, you should be able to set validated_data["number"] with your desired value (return the modified dict at the end of the "create" method).
I'm not sure, but i think you should remove "number" from your list of fields in the serializer since you're calculating it.

why model function created on django 3.1.10 doesn't Work on 3.2?

i created a proxy model in django 3.1.10 where i defined a function to make a copy of an object with all it's related objects through foreign key and it worked fine , but when i upgraded to django 3.2 the function only create a copy of the object without any related objects
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from ValPlanner.models import ProgramExecution
import datetime
from django.utils import timezone
#-----------------------------------------------------------------------------------------------------------------------------
class Record(models.Model):
title = models.CharField(max_length=50)
description = models.CharField(max_length=200)
execution = models.ForeignKey(ProgramExecution, on_delete=models.PROTECT,null=True)
timestamp = models.DateTimeField(auto_now_add=True)
creator = models.ForeignKey(User, on_delete=models.PROTECT)
record_type = models.CharField(max_length=50,default='template')
def __str__(self):
return self.title
class TemplateRecord(Record):
class Meta:
proxy = True
def create_report(self,creator,execution):
stages = self.stage_set.all();
self.record_type = 'record'
self.pk = None;
self.execution = execution
self.creator = creator
self.save();
for stage in stages :
samples = stage.sample_set.all()
stage.pk = None
stage.stage_type = 'record'
stage.record = self
stage.save();
for sample in samples :
tests = sample.testnum_set.all()
sample.sample_type = 'record'
sample.stage = stage
sample.sample_time = timezone.now()
sample.sampler = creator
for samplenum in range(sample.number_of_samples):
sample.pk = None
sample.save();
for test in tests :
test.pk = None
test.test_type = 'record'
test.sample = sample
test.tester = creator
test.save();
#-----------------------------------------------------------------------------------------------------------------------------
class Stage(models.Model):
record = models.ForeignKey(Record, on_delete=models.CASCADE)
title = models.CharField(max_length=50)
stage_type = models.CharField(max_length=50,default='template')
def __str__(self):
return self.title
#-----------------------------------------------------------------------------------------------------------------------------
class Sample(models.Model):
stage = models.ForeignKey(Stage, on_delete=models.CASCADE)
name = models.CharField(max_length=50)
sample_time = models.DateTimeField(default=timezone.now)
sample_location = models.CharField(max_length=50)
sampler = models.ForeignKey(User, on_delete=models.PROTECT)
sample_type = models.CharField(max_length=50,default='template')
number_of_samples = models.PositiveSmallIntegerField(null=True,default=1)
def __str__(self):
return self.name
#-----------------------------------------------------------------------------------------------------------------------------
class TestNum(models.Model):
sample = models.ForeignKey(Sample, on_delete=models.CASCADE)
name = models.CharField(max_length=50)
result = models.DecimalField(max_digits=15, decimal_places=5,null=True)
acceptance = models.CharField(max_length=50)
maximum_acceptance = models.DecimalField(max_digits=15, decimal_places=5)
minimum_acceptance = models.DecimalField(max_digits=15, decimal_places=5)
test_type = models.CharField(max_length=50,default='template')
tester = models.ForeignKey(User, on_delete=models.PROTECT)
def __str__(self):
return self.name
#-----------------------------------------------------------------------------------------------------------------------------
and my function is in view.py :
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib import messages
from django.contrib.auth.models import User
from django.urls import reverse
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView, TemplateView
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin, PermissionRequiredMixin
from .models import Record, TemplateRecord, Stage, Sample, TestNum
import datetime
from django.utils import timezone
class CreateEmptyReport (PermissionView, UpdateView):
model = TemplateRecord
permission_required = 'ETRecord.add_record'
fields = ['execution']
def get_object(self):
template_list = TemplateRecord.objects.filter(record_type='template')
return super().get_object(queryset = template_list)
def form_valid(self,form):
self.object.create_report(creator=self.request.user,execution=form.instance.execution)
return super().form_valid(form);
def get_success_url(self):
return reverse('ETRecord:report_details',kwargs= {'pk': self.object.id})
on old version it ould copy the record and all relaated stages , samples and tests but after update it only copies the record with no associated stages or samples
i tried to uninstall and re-install the older verison it works only on the older version , so how can i make it compatable with the new 3.2 version ?
You are creating the queryset before you copy the record itself, which seems fine
stages = self.stage_set.all();
But... Django won't resolve this queryset before it'll be accessed. Older versions of Django would probably just hold the ID of the parent element, so the queryset doesn't change after you change the ID on the self, but newer versions of Django are holding the entire object and extracting the ID of the object just before the queryset is actually executed on the database.
This causes your for loop to iterate over stages connected to the new record instance (you've just created it, so it's an empty queryset) instead of the old one.
To fix that issue, you can either force the execution of the queryset earlier (for example by casting it into a list) or by creating the queryset that won't use the self object, but the ID of the old object explicitly, for example:
stages = Stage.objects.filter(record_id=self.id);

All records displayed in many to many django app

Here my models:
django_zoom2/meetings/models.py
from django.db import models
from participants.models import Participant
class Meeting(models.Model):
topic = models.CharField(max_length=100)
uuid = models.CharField(max_length=100, default='xxxx')
participants = models.ManyToManyField(Participant)
def __str__(self):
return self.topic
django_zoom2/participants/models.py
from django.db import models
# from meetings.models import Meeting
class Participant(models.Model):
name = models.CharField(max_length=100)
email = models.CharField(max_length=100)
def __str__(self):
return self.email
..and the admin views:
django_zoom2/meetings/admin.py
from django.contrib import admin
from .models import Meeting
#admin.register(Meeting)
class MeetingAdmin(admin.ModelAdmin):
list_display = ['topic', 'uuid']
django_zoom2/meetings/admin.py
from django.contrib import admin
from .models import Participant
#admin.register(Participant)
class ParticipantAdmin(admin.ModelAdmin):
list_display = ['name', 'email']
I have a single association with the meeting blarg:
>>> from meetings.models import Meeting
>>> m= Meeting.objects.get(id=1)
>>> m
<Meeting: blarg>
>>> m.participants.all()
<QuerySet [<Participant: molly#myorg.org>]>
...but in the view here is what I see:
Why am I seeing all the participants email addresses in this view? I only want to see the associated email addresses; in this case, there should be one. Molly.
Ok, this should give you a list of Participants for each meeting as a Tabular inline. You can edit the extra=0 as needed.
In meetings/admin.py:
class AttendanceInline(admin.TabularInline):
model = Meeting.participants.through
extra = 0
#admin.register(Meeting)
class MeetingAdmin(admin.ModelAdmin):
inlines = [
AttendanceInline,
]
exclude = ('participants',)
More in the docs.

Django: How can i pass user input value from models.py to other file(ex entity_exctraction.py)

I am new to Django so please guide if i am going on wrong way.
Problem Defination:
To take user input AS string and perform various manipulations on the input and save the modified value in the database.I also want to show this modified input as a json response.
For temporary purpose i have created 4 fields namely ticker,open,close,volume.I want to transfer the open value from models.py to entity_exctraction.py file and multiply by 2 and save the updated value to the database.
I tried to write the same logic in the models.py (commented part) and its working fine.but what i want is to write all business logic in some different file.
Models.py
from django.db import models
from .entity_exctraction import Exctraction
class Stock(models.Model):
ticker = models.CharField(max_length=10)
open = models.FloatField()
close = models.FloatField()
volume= models.IntegerField()
open_val = Exctraction.update(open)
#def save(self, force_insert=False, force_update=False, using=None,
# update_fields=None):
# print(self.open)
# self.open = self.open * 2
# super(Stock, self).save()
def __str__(self):
return self.ticker
entity_exctraction.py
from django.db import models
class Exctraction():
def update(val):
val = val * 2.0
return val
serializers.py
from rest_framework import serializers
from .models import Stock
class StockSerializer(serializers.ModelSerializer):
class Meta:
model = Stock
print(type(model))
#model.open=model.open*2
#fields = ('ticker', 'volume')
fields = '__all__'

Populate Django ManyToManyField Options Based on Other Field in Admin

I am having problems filtering options for a ManyToManyField on the Django Admin Add screen based on input to another field on the same form. I am new to Django and have been unable to use any of the generic fixes described elsewhere because they are all slightly different than my situation. Here is my situation:
I have three models in my project: Class, Student, and AttendanceRecord. In the Django Admin, when adding an attendance record, I would like to change the options for the field Absent_Students based on the selection made for the field Associated_Class. So, for example, if Associated_Class "CS 450" is selected, the options for Absent_Students should change to only students whose class_list includes CS 450.
Here are my models:
from __future__ import unicode_literals
from django.db import models
from django.contrib.auth.models import User
from django.utils.encoding import python_2_unicode_compatible
import random, string
# Create your models here.
#This is the model for a student
#python_2_unicode_compatible
class Student(models.Model):
pass
Student_First_Name = models.CharField(max_length=200)
Student_Last_Name = models.CharField(max_length=200)
Student_ID_Number = models.CharField(max_length=200)
Student_Class = models.ForeignKey('Class', null=True)
def __str__(self):
return self.Student_Last_Name + ',' + self.Student_First_Name
# This is the model for a class
#python_2_unicode_compatible
class Class(models.Model):
class Meta:
verbose_name_plural = "Classes"
Class_Name = models.CharField(max_length=200)
Student_List = models.ManyToManyField('Student', related_name='class_list')
Professor = models.ForeignKey(User,null=True)
AddCode = models.IntegerField
pass
def __str__(self):
return self.Class_Name
def getName(self):
return self.Class_Name
def getProfessor(self):
return self.Professor.id
def getProf(self):
return self.Professor
def getStudents(self):
return self.Student_List
#This is the model for attendance records
class AttendanceRecord(models.Model):
class Meta:
verbose_name = "Attendance Record"
Associated_Class = models.ForeignKey(Class, on_delete=models.CASCADE, related_name='Attendance_Records')
Date = models.DateField()
Absent_Students = models.ManyToManyField('Student', blank=True)
Present_Students = models.ManyToManyField('Student', related_name='a')
def get_associated_class_id(self):
return self.Associated_Class
def __str__(self):
return self.Associated_Class.__str__() + ' on date ' + self.Date.__str__(self)
I have tried doing this by editing the AttendanceRecordAdminForm class and AttendanceRecordAdmin class. My problem is that when setting the self.fields['Absent_Students].queryset I do not know how to access the currently selected Associated_Class on the form. I keep getting an error that "AttendanceRecord has no Associated_Class". Here are those classes just discussed in their entirety:
class AttendanceRecordAdminForm(forms.ModelForm):
class Meta:
model = AttendanceRecord
fields = '__all__'
def __init__(self, *args, **kwargs):
super(AttendanceRecordAdminForm, self).__init__(*args, **kwargs)
instance = kwargs.get('instance', None)
self.fields['Absent_Students'].queryset = Student.objects.filter(class_list__id=self.instance.get_associated_class_id())
self.fields['Present_Students'].queryset = Student.objects.filter(class_list__id=1)
class AttendanceRecordAdmin(admin.ModelAdmin):
form = AttendanceRecordAdminForm
filter_horizontal = ('Absent_Students', 'Present_Students',)
Basically, I am looking for a way to access the currently entered Associated_Class on the admin form so I can properly filter the queryset.
After hours more of online searching I finally found what I needed. A chained ManyToMany from the smart_select app makes this very easy. This link: How to use django-smart-select describes the install process and also links to the documentation for using it once it is installed. Hopefully this helps some others as well.

Categories

Resources