Treating a model as an instance of imported class in Django - python

I have a class for manipulating certain data.
In that class I calculate and validate the data.
Then, is there a way to apply this calculation and validation to an instance of Django model?
Ideally, I would like to store an instance of the imported class in the database.
I came up with two methods.
1.Make another class to connect model and imported class.
2.Write validation and calculations directly in models.py.
Which is better?
Is there another good way?
If 2 is good, how to do that?
Here is a very simple example.
# in models.py
class Person(models.Modes):
name = models.CharField()
gender = models.CharField()
age = models.IntegerField()
valid = models.NullBooleanField(blank=True)
# in other files
class AboutPerson:# I want to apply this class to above model instance.
def __init__(self, name, gender, age):
self.name =name
self.gender = gender
self.age = age
self.valed = None
def validate_age(self):
self.valid = self.age >= 20
def other_validate(self, age):
self.valid = self.age >= age
In this example, I want to apply change of self.valid to model automatically.
Now, I adopt 2. This is also a simple example.
class FromModel:
'''
obj:object obtained from database
This class passes the value received from the database to the class imported from the module.
Attribute:
'''
def __init__(self, obj):
self.obj = obj
self.instance = AboutPerson(name=obj.name, gender=obj.gender, age=obj.age)
def update(self):#use this method after some validation has done.
self.obj.valid = self.instance.valid
self.obj.save()
I wish you could understand my poor explanation...
Thank you for helping me.

As Alan Hoover said, if you aren't planning on needing to validate the form data from user input then I don't think you should use a form.
My suggestion is to keep the validation of the model in the model. There is a built in method for validation: Model.clean(). So using your example:
models.py
class Person(models.Model):
name = models.CharField(max_length=64)
gender = models.CharField(max_length=16)
age = models.PositiveIntegerField()
def clean(self):
if self.age < 21:
raise ValidationError('No beer for you!')
if self.name == 'Charles':
self.name = 'Charley'
views.py
def my_function():
# ...
my_person_obj.clean()
my_person_obj.save()
Let me know if I misunderstood your question.

Related

Change representation of Superclass without field depending of Subclass object (__str__)

I am running a django app and have a setup like this:
ModelSuper(models.Model):
class Meta:
abstract = False
ModelSub1(ModelA):
name = models.CharField(...)
def __str__:
return self.name
ModelSub2(ModelA)
name = models.CharField(...)
def __str__:
return self.name
ModelForeign(models.Model):
element = models.ForeignKey(ModelA)
def __str__:
return self.name
So ModelForeign has a FK to ModelSuper. What happens now is that when I create an instance of ModelForeign I can choose if it belongs either to ModelSub1 or to ModelSub2. But the string representation is ModelSuper Onject (3) where (3) is the id.
Normally I can change this representation by overwriting the __str__ method on the model, but since I do not have any fields on the Supermodel I can't return anything.
What I tried:
I have already implemented the __str__ method in the Submodels but that does not help.
I wanted to make the Super model abstract. But this does not let me point FKs to the Supermodel, so I can't do this. My setup requires this FK
I used a generic FK with django's ContentType framework. This is also not an option because it messes completely with my app and is also not recommended from an SQL perspective.
Also when I do API-calls I get ModelSuper Onject (3) back instead of a human-readable name.
Is there a way to do what I intend to do? Thanks in advance for help and hints. Very much appreciated!
EDIT1: What I tried thanks to Abdul's help:
class ModelA(models.Model):
class Meta:
abstract = False
TYPE_CHOICES = [('sub1', 'sub1'), ('sub2', 'sub2')]
type_model = models.CharField(max_length=50, choices=TYPE_CHOICES, null=True, blank=True)
def __str__(self):
if self.type_model == "sub1":
return "sub1"
elif self.type_model == "sub2":
return "sub2"
else:
return "unkown"
I am not understanding how your foreign key works as model inheritance means the tables are separate. How about trying something like this:-
ModelA(models.Model):
TYPE_CHOICES = [('Sub1', 'ModelSub1'), ('Sub2', 'ModelSub2')]
model_type = models.CharField(max_length=4, choices=TYPE_CHOICES)
def __str__:
# Return string representation using if-else
class Meta:
abstract = False
ModelSub1(ModelA):
name = models.CharField(...)
model_a = models.ForeignKey(ModelA, on_delete=models.CASCADE)
def __str__:
return self.name
ModelSub2(ModelA)
name = models.CharField(...)
model_a = models.ForeignKey(ModelA, on_delete=models.CASCADE)
def __str__:
return self.name
ModelForeign(models.Model):
element = models.ForeignKey(ModelA)
def __str__:
return self.name

Why create and return an instance of a class within the same class?

Noob here.
Actually want to understand the concept behind returning an instance of a class within the class itself. Code snippet below don't actually work, but want to understand idea behind code in the get_student() method (more especially the last two lines).
import database
class Student(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
def save(self):
"""Save student data to database"""
pass
#staticmethod
def get_student(std_id):
"""Retrieve student from database"""
query = "query" + std_id
student = database.Database.get_one(query)
return Student(name=student['name'], gender=student['gender'])

create value to a model from another model's method

I'm trying to create a new table (Payslip model) that will contain the computed salary on an employee in a cutoff.
I want Payslip.salary to get value from Employee.compute_pay()
Given the example in the url link above, what should my views.py look like?
Is this the best approach to this kind of process? or Is there any library that can help with what I want to do here?
https://imgur.com/a/wVG5qrd
model.py
class Employee(models.Model):
name = models.CharField(max_length=20)
rate = models.IntegerField()
absent = models.IntegerField()
def __str__(self):
return self.name
def compute_pay(self):
daily = rate / 20
return rate - (daily*absent)
class Payslip(models.Model):
name = models.CharField(max_length=20)
salary = models.IntegerField()
def __str__(self):
return self.name
views.py
def compute(request):
if request.method == "POST":
return render(request,'payroll/compute/compute.html')
I do not think there is a need for another model Payslip, also you have no ForeignKey connections between the two models for it to work.
Considering your requirement, property decorator should work. Read up on how #property works. Basically, it acts as a pseudo model field, keep in mind the value of this field is not stored anywhere in the database, but it is tailor-made for situations like this where the field changes based on other fields and is needed for read-only.
Try this
class Employee(models.Model):
name = models.CharField(max_length=20)
rate = models.IntegerField()
absent = models.IntegerField()
def __str__(self):
return self.name
#property
def compute_pay(self):
daily = self.rate / 20
return (self.rate - (daily*self.absent))
you can get the employee's salary by Employee.compute_pay just like any other model field

Initializing Python class object with external data

Suppose a "person" class contains name, age and phone number.
When creating a person object, I would like to set phone number by looking up an external phone book rather than explicitly passing a phone number.
Option 1: Store phone book as a class variable
class person():
phonebook = {}
def __init__(self, name, age):
self.name = name
self.age = age
self.phone = self.phonebook[self.name]
person.phonebook = {'dan':1234}
dan = person('dan', 30)
Option 2: Create a class object without phone number then have a separate function to load it.
class person():
def __init__(self, name, age):
self.name = name
self.age = age
def loadphone(self, phone):
self.phone = phone
phonebook = {'dan':1234}
dan = person('dan',30)
dan.loadphone(phonebook['dan'])
Both solutions do not seem optimal. Option 1, every person carries a phone book (unnecessarily). Option 2 requires 2-step initialization.
Is there a better way to create a person object without 1) explicitly passing a phone number or phone book during initialization, 2) storing phone book as a class variable, and 3) requiring a multi-step initialization?
As discussed in this post, defining a variable outside of any methods in the class, while still being defined in a class makes it a static variable, such as the one you have:
class person():
phonebook = {}
This means that there is a single phonebook which all instances of the class refer to
person.phonebook{'dave':1234, 'joey':5678}
dave = person('dave', 30)
joey = person('joey', 23)
There is still only the one universal phonebook that all instances refer to. The one thing to change in that code is that you should not define it as self.phonebook['dave'] so it should look like
class person():
phonebook = {}
def __init__(name, age):
self.name = name
self.age = age
self.number = phonebook[name]
Are you wanting to optionally define a phone number for a Person? You could do something like below:
class Person():
def __init__(self, name, age, phone=None):
self.name = name
self.age = age
self.phone = phone
dan = Person('dan',30, phone=1234)
stan = Person('stan', 60)
Firstly, as for me, it's too wide question and very depend on task. In one case you can access to PhoneBook, in another - it's bad idea (e.g. PhoneBook load data from server and creating 1000 of Person will produce 1000 requests).
Secondary, their is next approach:
class BasicPerson():
def __init__(self, name, age):
self.name = name
self.age = age
def ToString(self):
return('some code')
class PersonWithPhone():
def __init__(self, basicPerson, phone):
self.basicPerson = basicPerson
self.phone = phone
def ToString(self):
return('another code ' + self.basicPerson.ToString())
person = PersonWithPhone(BasicPerson('', ''), '11111')
It's just example and may seems useless, but in many situations you can extract some core actions (ToString, for example) and than wrote small decorators that expand each other.

Relationships with pairs of interrelated models

Let's say I have 3 models in Django: Person, Workfield and SubWorkfield.
A person can have many workfield-s and many subWorkfield-s as well, but the subWorkfield-s must be related to their parent workfield-s ( which the person must be related to).
So how do I enforce that whenever a person is related to a subWorkfield then he must also be related to that subWorkfield's parent workfield?
Here's what I have so far, but I don't think it enforces the relationship:
class Person(models.Model):
name = models.CharField(max_length=200)
workfield = models.ManyToManyField(Workfield)
subworkfield = models.ManyToManyField(SubWorkfield)
class Workfield(models.Model):
name = models.CharField(max_length=200)
class SubWorkfield(models.Model):
name = models.CharField(max_length=200)
workfield = models.ForeignKey(Workfield)
I need to have the workfields and subWorkfield-s decoupled because a person can belong to a workfield without any subWorkfield-s.
There really isn't any good way to do what you want purely with the table structure itself. However, if you do the following:
class Person(models.Model):
name = models.CharField(max_length=200)
workfields = models.ManyToManyField(Workfield)
subworkfields = models.ManyToManyField(SubWorkfield)
def add_subworkfield(self, subworkfield):
if subworkfield.workfield not in self.workfields:
return False
else:
self.subworkfields.append(subworkfield)
class Workfield(models.Model):
name = models.CharField(max_length=200)
class SubWorkfield(models.Model):
name = models.CharField(max_length=200)
workfield = models.ForeignKey(Workfield)
And then you will just use the add_subworkfield method when adding subworkfields to Persons.
Alternatively, inside of the Person model you could override the save method:
class Person(models.Model):
...
def save(self, *args, **kwargs):
for subworkfield in self.subworkfields:
if subworkfield.workfield not in self.workfields:
return
super(Person, self).save(*args, **kwargs)
If it's a small app and it's just you, the first way should suffice. However, if you're working on something larger with multiple people, overriding save would be a safety incase someone doesn't use the add_subworkfield method.

Categories