foreign key in django-import-export - python

First of all pardon me for my poor English. I'm using django-import-export to upload excel file into my student model that has foreign key relationship with university model
student.. models.py:
class Student(models.Model):
institution = models.ForeignKey(University, on_delete=models.CASCADE)
id = models.CharField(max_length=200, primary_key=True)
first_name = models.CharField(max_length=200)
middle_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
age = models.IntegerField()
faculty = models.CharField( max_length=200, null=True, blank=True)
program = models.CharField( max_length=200, null=True, blank=True)
def __str__(self):
return self.first_name
university.. models.py:
class University(models.Model):
name = models.CharField(max_length=300)
email = models.EmailField(max_length=255, unique=True)
phone_no1 = PhoneNumberField()
phone_no2 = PhoneNumberField(blank=True)
fax_no = PhoneNumberField()
website = models.URLField(max_length=200)
pob = models.IntegerField()
city = models.CharField(max_length=200, blank=True)
logo = models.ImageField(upload_to="logos/", blank=True, null=True)
def __str__(self):
return self.name
After reading django-import-export documentation about ForeignKeyWidget
I edited my resources.py file as following and it works fine when I upload excel file that contain institution id and other student information
resources.py
class StudentResource(resources.ModelResource):
institution = fields.Field(
column_name='institution',
attribute ='institution',
widget = ForeignKeyWidget(University, 'id')
)
class Meta:
model=Student
But I don't want to include institution id into my excel file while I am uploading, because I can find the institution id from Registrar Staff logged in since the RegistrarStaff model has foreign key relationship with university model
class RegistrarStaff(models.Model):
user = models.OneToOneField(User, on_delete = models.CASCADE, primary_key = True)
university = models.ForeignKey(University, on_delete=models.CASCADE, null=True)
phone_number = models.CharField(max_length=20)
def __str__(self):
return str(self.user)
This is the way I able to find institution id based on whose university Registrar Staff is logged in and passed the value into resource like on number 4 in views.py
views.py:
def upload(request):
if request.method == 'POST':
loged=request.user.id
univ = RegistrarStaff.objects.get(pk=loged).university_id
student_resource = StudentResource(institution=univ)
dataset = Dataset()
new_student = request.FILES['myfile']
if not new_student.name.endswith('xlsx'):
messages.info(request,'Wrong format')
return render(request, 'upload.html')
imported_data = dataset.load(new_student.read(), format='xlsx')
for data in imported_data:
value = Student(
data[0],
data[1],
data[2],
data[3],
data[4],
data[5],
)
value.save()
return render(request, 'upload.html')
And I intialized it in resources.py as follow
resources.py:
class StudentResource(resources.ModelResource):
def __init__(self, *args, **kwargs):
self._institution=kwargs.pop('institution', None)
super().__init__(*args, **kwargs)
print(self._institution)
class Meta:
model=Student
So, is there anyway I can set the value of institution id = self._institution so as to able to upload excel file that doesn't contain institution id????

You are on the right lines. The way to do this is to pass the institution instance (or id) into the constructor, which is what you have already. All you need to do is to set the institution instance onto the Student model instance before it is saved:
class StudentResource(resources.ModelResource):
def __init__(self, *args, **kwargs):
self._institution=kwargs.pop('institution', None)
super().__init__(*args, **kwargs)
print(self._institution)
def before_save_instance(self, instance, using_transactions, dry_run):
setattr(instance, "institution", self._institution)
class Meta:
model=Student
However, you are bypassing the django-import-export framework by calling Student.save() directly. Don't do this. Use the framework to handle instance creation for you (docs):
result = student_resource.import_data(imported_data, dry_run=True)

Related

Model Query Django

I'm trying to query fields from another model in Django .
I'm able to fetch the user information using the following method
def student(self, *args, **kwargs):
std_name = Attendance.objects.get(roll=self.roll)
return std_name.name
Now I need to find the student's assigned teacher from a different table, I need to fetch it only using the student name which I got using student() as I don't have any variable in my current table to reference it to fetch that information from the new table.
def teacher(self, *args, **kwargs):
teacher_name = staff.objects.get(student=self.student)
return teacher_name.name
But the above method is not working properly and is not populating the fields in my admin.py page . Can someone help me in fixing this issue
#Attendance model
class Attendance(models.Model):
class Meta:
verbose_name = 'Attendance'
verbose_name_plural = ''Attendance''
Group = models.TextField(max_length=15, blank=True, null=True)
Year = models.IntegerField(blank=True,null=True)
roll = models.IntegerField(blank=True,null=True)
Date = models.TextField(max_length=15, blank=True, null=True)
name = models.TextField(max_length=10, blank=True, null=True)
Presence = models.TextField(max_length=15, blank=True, null=True)
def student(self, *args, **kwargs):
std_name = Attendance.objects.get(roll=self.roll)
return std_name.name
def teacher(self, *args, **kwargs):
teacher_name = staff.objects.get(student=self.student)
return teacher_name.name
#staff model
class staff(models.Model):
class Meta:
verbose_name = staff
verbose_name_plural = ''staff''
name = models.TextField(max_length=10, blank=True, null=True)
degree = models.TextField(max_length=15, blank=True, null=True)
subject = models.IntegerField(blank=True,null=True)
experience = models.IntegerField(blank=True,null=True)
student = models.TextField(max_length=15, blank=True, null=True)

How to add manytomany relation in other class . In my case in InfoCreateView class

I am making a CV page,
I want to link my Skill, Language etc class(table) to Main Person table/class,
But for that, I need to submit skill table first because my person table contains the foreign key for skills.
But as per CV form name & personal info comes first.
Also, I can put the whole form on one page but I want to go to the next page for each sub information, so is it possible to pass the request data from one class-based view to another class-based view.
models.py
from django.db import models
from django.core.validators import MinLengthValidator
from django.conf import settings
import datetime
class Workexperience(models.Model):
work = models.CharField(null=True, blank=True,
max_length=256,
help_text='eg: Juniorengineer: at L&T ')
person = models.ForeignKey('Person', on_delete=models.CASCADE, blank=True, null=False, default=1 )
def __str__(self):
return self.work
class Education(models.Model):
school = models.CharField(max_length=200)
college = models.CharField(null=True, blank=True,max_length=200)
person = models.ForeignKey('Person', on_delete=models.CASCADE, blank=True, null=False, default=1 )
def __str__(self):
return self.school
class Skills(models.Model):
skill = models.CharField(
max_length=256,
help_text='Add skills sperated by commas eg: programming, Matlab')
person = models.ForeignKey('Person', on_delete=models.CASCADE, blank=True, null=False, default=1 )
def __str__(self):
return self.skill
class Languages(models.Model):
language = models.CharField(
max_length=256,
help_text='Add language sperated by commas eg: English, Gujarati')
person = models.ForeignKey('Person', on_delete=models.CASCADE, blank=True, null=False, default=1 )
def __str__(self):
return self.language
class Person(models.Model):
name = models.CharField(
max_length=100,
help_text='Enter a name (e.g. Harry Virani)',
validators=[MinLengthValidator(2, "It must be greater than 1 character")]
)
picture = models.BinaryField(null=True, blank=True, editable=True)
content_type = models.CharField(max_length=256, null=True, blank=True,
help_text='The MIMEType of the file')
profession = models.CharField(
max_length=100,
validators=[MinLengthValidator(2, "It must be greater than 1 character")]
)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, default='')
address = models.CharField(max_length=256)
email = models.EmailField(max_length = 254)
phone = models.CharField(
max_length=15,
help_text='Enter a phone number like this (e.g. +91000000000)',
validators=[MinLengthValidator(10, "It must be greater than 10 character")] )
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
facebook = models.URLField(null=True, blank=True, max_length=200,
help_text='enter your facebook URL' )
instagram = models.URLField(null=True, blank=True, max_length=200,
help_text='enter your instagram link URL' )
linkedin = models.URLField(null=True, blank=True, max_length=200,
help_text='enter your Linked link URL' )
skill = models.ManyToManyField(Skills, related_name='skills', default=1)
language = models.ManyToManyField(Languages, related_name='languages', default=1)
edu = models.ManyToManyField(Education, default=1,related_name='edu' )
work = models.ManyToManyField(Workexperience,default=1, blank=True, related_name='works')
# Shows up in the admin list
def __str__(self):
return self.name
views.py
I want to save it in another class which is for creating skill & other models.
class PersonCreateView(LoginRequiredMixin, View):
template_name = 'MYP/form.html'
success_url = 'MYP:myp_create_info'
def get(self, request, pk=None):
personform = PersonForm()
ctx = { 'personform': personform}
return render(request, self.template_name, ctx)
def post(self, request, pk=None) :
# if 'personform' in request.POST:
personform = PersonForm(request.POST, request.FILES or None)
if not personform.is_valid():
ctx = {'personform': personform}
return render(request, self.template_name, ctx)
pform = personform.save(commit=False)
#adding onwer
pform.owner = self.request.user
pform.save()
return redirect(self.success_url, pform.id)
class InfoCreateView(LoginRequiredMixin, View):
template_name = 'MYP/form2.html'
success_url = reverse_lazy('MYP:all')
def get(self, request, pk):
person = get_object_or_404(Person,id=pk)
skill= SkillsForm()
skill_list = Skills.objects.filter(person=person)
ctx = { 'skill':skill, 'skill_list':skill_list }
return render(request, self.template_name, ctx)
def post(self, request, pk):
if 'skill' in request.POST:
skill = SkillsForm(request.POST or None)
if not skill.is_valid() :
ctx = { 'skill':skill}
return render(request, self.template_name, ctx)
person = get_object_or_404(Person,id=pk)
print(person)
skill = Skills(skill=request.POST['skill'], person=person)
skill.save()
print(skill.person)
return redirect('MYP:myp_create_info', pk=pk)
forms.py
class PersonForm(forms.ModelForm):
max_upload_limit = 2 * 1024 * 1024
max_upload_limit_text = naturalsize(max_upload_limit)
# Call this 'picture' so it gets copied from the form to the in-memory model
# It will not be the "bytes", it will be the "InMemoryUploadedFile"
# because we need to pull out things like content_type
picture = forms.FileField(required=False, label='File to Upload <= '+max_upload_limit_text)
upload_field_name = 'picture'
# Hint: this will need to be changed for use in the ads application :)
class Meta:
model = Person
fields = ['name', 'profession', 'picture', 'address', 'email', 'phone','facebook','linkedin','instagram'] # Picture is manual
# Validate the size of the picture
def clean(self) :
cleaned_data = super().clean()
pic = cleaned_data.get('picture')
if pic is None : return
if len(pic) > self.max_upload_limit:
self.add_error('picture', "File must be < "+self.max_upload_limit_text+" bytes")
# Convert uploaded File object to a picture
def save(self, commit=True) :
instance = super(PersonForm, self).save(commit=False)
# We only need to adjust picture if it is a freshly uploaded file
f = instance.picture # Make a copy
if isinstance(f, InMemoryUploadedFile): # Extract data from the form to the model
bytearr = f.read();
instance.content_type = f.content_type
instance.picture = bytearr # Overwrite with the actual image data
if commit:
instance.save()
return instance
class WorkexperienceForm(forms.ModelForm):
class Meta:
model = Workexperience
fields = ['work']
class EducationForm(forms.ModelForm):
class Meta:
model = Education
fields = ['school','college']
class SkillsForm(forms.ModelForm):
class Meta:
model = Skills
fields = ['skill']
class LanguagesForm(forms.ModelForm):
class Meta:
model = Languages
fields = ['language']
Ignore the rest of the code it is just for image handling....
This is what I want to do but I know it is the wrong format
I want to just add id for everything later.
In my opinion, your models are messed up. Here is how I would have write them :
class WorkExperience(models.Model):
work = models.CharField(
blank=True,
max_length=256,
help_text='eg: Juniorengineer: at L&T'
)
def __str__(self):
return self.work
class Education(models.Model):
school = models.CharField(max_length=200)
college = models.CharField(blank=True, max_length=200)
def __str__(self):
return self.school
class Skill(models.Model):
name = models.CharField(
max_length=256,
help_text='Add a skill name (eg: Programming)'
)
def __str__(self):
return self.name
class Language(models.Model):
name = models.CharField(
max_length=256,
help_text='Add a language name (eg: Gujarati)'
)
def __str__(self):
return self.name
class Person(models.Model):
name = models.CharField(
max_length=100,
help_text='Enter a name (e.g. Harry Virani)',
validators=[MinLengthValidator(2, "It must be greater than 1 character")]
)
# [...Other fields...]
skills = models.ManyToManyField(Skill, related_name='persons', blank=True)
languages = models.ManyToManyField(Language, related_name='persons', blank=True)
educations = models.ManyToManyField(Education, related_name='persons', blank=True)
work_experiences = models.ManyToManyField(WorkExperience, related_name='persons', blank=True)
def __str__(self):
return self.name
Then I need to see your forms.py to better understand how you handle it in your view.

Django Rest Framework : Setting foreign key value in serializer

I have the following models:
class School(models.Model):
id = patch.BigAutoField(primary_key=True)
name = models.CharField('Name', max_length=100)
address = models.CharField('Address', max_length=500, blank=True, null=True)
class Child(BaseModel):
id = patch.BigAutoField(primary_key=True)
name = models.CharField('Name', max_length=100, blank=True, null=True)
school = models.ForeignKey('User', blank=True, null=True, db_constraint=False, db_index=True, on_delete=models.CASCADE, default=None)
I have the following serializers :
class SchoolSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
name = serializers.CharField(required=True, max_length=100)
address = serializers.CharField(required=False, max_length=400)
def create(self, validated_data):
return School.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.name = validated_data.get('name', instance.name)
instance.address = validated_data.get('address', instance.address)
instance.save()
return instance
class Meta:
model = School
fields = ('id', 'name', 'address')
class ChildSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
name = serializers.CharField(required=False, max_length=100, allow_blank=False)
school = SchoolSerializer()
def create(self, validated_data):
return Child.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.name = validated_data.get('name', instance.name)
instance.school = validated_data.get('school', instance.school)
instance.save()
return instance
Now the problem that I am facing is that when I saving any value in my child table using serializer then the value of school is showing null in my database but in my request object I am getting value for school_id.
Since this school = SchoolSerializer() school would be a fully serialized object, not scalar value.
Take a look at this example it should help: Writable nested serializer with existing objects using Django Rest Framework 3.2.2
You got to convert serialized object school manually into scalar valued school PK to fill school_id FK field. Or just remove school = SchoolSerializer(), then ChildSerializer will start serializing this field as scalar-valued (but still FK) and thus will make it simply writable directly to the school_id field. The rest of the code should work well.

Django junction table with extra foreignkey

I have the following models
class Company(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(
max_length=500,
blank=True,
help_text='Any text to describe a company'
)
url = models.URLField('company URL', blank=True, null=True)
email = models.EmailField(blank=True, null=True)
created_on = models.DateTimeField('date created', default=timezone.now)
class Meta:
verbose_name = 'company'
verbose_name_plural = 'companies'
ordering = ['name', '-created_on']
def __repr__(self):
return '<Company {0.name}>'.format(self)
def __str__(self):
return self.name
class Project(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(
max_length=500,
blank=True,
help_text='Any text to describe the project'
)
company = models.ForeignKey(
Company,
on_delete=models.PROTECT,
)
created_on = models.DateTimeField('date created', default=timezone.now)
class Meta:
verbose_name = 'project'
verbose_name_plural = 'projects'
ordering = ['-created_on', 'company']
permissions = (
("can_view_project",
"Can view all project related work"),
)
def __repr__(self):
return '<Project {0.name}>'.format(self)
def __str__(self):
return self.name
class Worker(models.Model):
description = models.TextField(
max_length=500,
blank=True,
help_text='Optional. Describe what the worker does or who they are'
)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
company = models.ForeignKey(Company)
class Meta:
order_with_respect_to = 'user'
def __repr__(self):
return '<Worker {0.id}'.format(self)
def __str__(self):
return self.user.get_fullname()
The problem
I would like to add a ManyToMany relationship between Project and Worker so that I can view
a list of workers under a certain project. However, I want to make sure that a worker can only
be added to a project if they are both part of the same company.
I was planning on using a junction table with a ForeignKey to both of their company attributes,
but according to the django docs, a foreignkey can only be used once per model
(https://docs.djangoproject.com/en/1.10/topics/db/models/#extra-fields-on-many-to-many-relationships)
How do I make sure that the many to many relationship between the two tables is limited to the same company?
Is there perhaps another way to ensure that workers cannot work on projects outside of their own company?
Assuming you define the many to many relationship this way in the Project model:
workers = ManyToManyField(Worker)
Assuming you have a model form named ProjectForm to create or modify projects. You can define a clean function in this form:
def clean(self):
cleaned_data = super(ProjectForm, self).clean()
for w in cleaned_data['workers']:
if w.company.id != cleaned_data['company'].id:
self.add_error('workers', your_error_message)
break
return cleaned_data
Hope this help.

how to access auto increment id of any model using form in django?

models.py
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=50)
city = models.CharField(max_length=60)
state_province = models.CharField(max_length=30)
country = models.CharField(max_length=50)
def __unicode__(self):
return self.name
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField()
def __unicode__(self):
return u'%s %s' % (self.first_name, self.last_name)
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author, blank=True)
publisher = models.ForeignKey(Publisher, blank=True, null=True)
def __unicode__(self):
return self.title
forms.py
class Books(forms.ModelForm):
class Meta:
model = Book
fields = ('title', 'authors', 'publisher',)
class Authors(forms.ModelForm):
class Meta:
model = Author
fields = ('first_name', 'last_name', 'email',)
class Publishers(forms.ModelForm):
class Meta:
model = Publisher
fields = ("id","name", 'address', 'city', 'state_province', 'country',)
views.py
def demo(request):
form1 = Books(request.POST)
form2 = Authors(request.POST)
form3 = Publishers(request.POST)
if (form1.is_valid() & form2.is_valid() & form3.is_valid()):
form3.save()
form2.save()
// Here I want to access id of just saved data of Model Publisher and Model Authors
form1.cleaned_data['publisher'] = form3.data['id']
return render(request, 'files/demo.html', {'form1': form1, 'form2': form2, 'form3': form3})
In Above code I want to save all data of all models in single view But errro is that Book model has ForenignKey relation with Publisher and Publisher doens't not have any unique data to identify the ID of just saved data. So My real question is that how can I access the Publisher and Author ID that data is saved using `
form3.save() and form2.save()
`I'm very confused to save multiple model data with same time with handle forenignkey relation between them.
save() returns the object, so you just need to do
publisher_obj = form3.save()
author_obj = form2.save()
book_obj = form1.save(commit=False)
book_obj.publisher = publisher_obj
book_obj.save()
book_obj.authors.add(author_obj)
and remove the 'id' from the form. You don't need it.

Categories

Resources