Here is the gist of what I am trying to achieve. I have a list of users which has a fixed list of attributes
EmployeeID
Employee
FirstName
LastName
SSN
This can be easily be created in models and a UI can be generated to enter these values
I have another list of user attributes that is not fixed but is dynamic. As an example, I'd like to capture more info if employee is married, if he is a graduate. Basically the list of additional properties for employees is infinite and will be specific to each employee. It is also changing constantly, so cannot plan ahead for it. It obviously does not make sense to create a table with all the fields like this
EmployeeID (foreignKey Employee table)
IsMarried
SpouseName
IsGraduate
GraduationYear
GraduationMajor
GraduationMinor
IsCertified
...
I think a NoSql db like mongodb makes sense here. I believe I can save employeeID of the employee and only the relevant info of the user in the mongodb record. How do I create a UI so the HR folks can have a form to edit/enter this info? Each user will have a different set of attributes and corresponding text fields to display/edit. Any suggestions?
For dynamic attributes using a relational database you could try something like this:
class Employee( models.Model ):
firstname = models.CharField( max_length=50 )
lastname = models.CharField( max_length=50 )
ssn = models.IntegerField()
class EmployeeAttribute( models.Model ):
ATTRIBUTE_CHOICES = (
(0, 'is_married'),
(1, 'spouse_name'),
(2, 'is_graduate'),
...
)
name = models.IntegerField( max_length=3, choices=ATTRIBUTE_CHOICES )
value = models.CharField( max_length=255 )
employee = models.ForeignKey( 'Employee' )
Whenever you have a new attribute that you would like to add, just append it to the tuple of attribute choices incrementing the last number.
If you're aiming for something that doesn't require writing extra code every time a new attribute needs to be added, then try something like this:
class Employee( models.Model ):
firstname = models.CharField( max_length=50 )
lastname = models.CharField( max_length=50 )
ssn = models.IntegerField()
class Attribute( models.Model ):
name = models.CharField( max_length=50 )
class EmployeeAttribute( models.Model ):
attribute = models.ForeignKey( 'Attribute' )
employee = models.ForeignKey( 'Employee' )
With this approach you could at least create an area that would allow you to create new attributes either via the Django admin, or by a custom template.
To get the extended attributes for an employee would be as simple as this:
employee = Employee.objects.get( firstname='John', lastname='Doe' )
extended_attributes = employee.employee_attribute_set.all()
Related
I have tables that share information in a single related table via foreign keys. The relationships work as expected, however, I'm trying to figure out how to automatically populate fields that are then used to filter the results. I hope the example below illustrates what I'm trying to do.
In the Models:
class UtilType(models.Model):
name = models.CharField()
description = models.CharField()
# following boolean fields used to filter table
is_address = models.BooleanField(default=False)
is_phone = models.BooleanField(default=False)
is_email = models.BooleanField(default=False)
is_domain = models.BooleanField(default=False)
class Address(models.Model):
address_type = models.ForeignKey(
UtilType,
on_delete=models.SET_NULL,
blank=True,
null=True,
related_name="addresses",
limit_choices_to={'is_address': True}
)
class PhoneType(models.Model):
phone_type = models.ForeignKey(
UtilType,
on_delete=models.SET_NULL,
blank=True,
null=True,
related_name="addresses",
limit_choices_to={'is_phone': True}
)
... more models with similar schema
In the Admin:
class ContactPhoneNumberInline(admin.StackedInline):
model = PhoneNumber
min_num = 0
max_num = 5
extra = 0
exclude = ["company"]
fields = (
("phone_type", "country", "phone_number"),
)
class ContactEmailAddressInline(admin.StackedInline):
model = EmailAddress
min_num = 0
max_num = 5
extra = 0
exclude = ["company"]
fields = (
("email_type", "email_address"),
)
.... more inlines w/ similar structure
#admin.register(Contact)
class ContactAdmin(admin.ModelAdmin):
fields = (
"company",
("name_first", "name_middle", "name_last",),
("name_salutation", "name_suffix", "title"),
)
inlines = [
ContactPhoneNumberInline,
ContactEmailAddressInline,
ContactDomainInline,
ContactAddressInline
]
When editing a contact, the action is as expected. I can add information to each type and the types show filtered as directed in the ForeignKeys.
However, the admin window for UtilType has the boolean selection fields: is_address, is_phone, is_email, is_domain so the user must select this to be filtered correctly. I can hide these fields, with the exclude method.
But how do I automatically populate the right boolean (=True) based on which inline is currently being used?
Would it be best to use a save override method in the models, in the admin, or is there a better way to do this?
I haven't found a way to do this in the Django admin. If someone knows how it would be good information. I'll deal with the action in the front end once it's developed. I'm not sure it's worth the effort in the admin.
I have a SaaS project which many companies will use and within the companies there will be employees which will be applied to one of two groups: Supervisor or Non-Supervisor. This will probably look as such:
class EmployeeGroup(Group):
....
Each company will be able to create their own EmployeeType which falls into one of the EmployeeGroups. e.g. an EmployeeType may be Food and Beverage Supervisor which is an EmployeeGroup: Supervisor:
class EmployeeType(models.Model):
....
employee_type = models.CharField(
max_length=32,
default='Server'
)
employee_group = models.ForeignKey(
EmployeeGroup,
null=True
)
company = models.ForeignKey(
Company,
on_delete=models.CASCADE,
null=True
)
And obviously each User will have an EmployeeType:
class User(AbstractUser):
....
employee_type = models.ManyToManyField(
EmployeeType,
null=True,
)
In this situation, can several companies share EmployeeGroups without sharing EmployeeTypes? My concern is that if an EmployeeType: Server is created at Company A then Company B will not be able to create an EmployeeType: Server as well. Or that if they do then information will get crossed. It wuold help to have an understanding of how the database works but I do not. Any links to external resources specific to this situation would be greatly appreciated!
To simplify and facilitate your requests to DB you may add one more column in your Company table, and write in it every new employee_groups when a new employee is hired:
class Company(models.Model):
...
employee_groups = models.ManyToMany(EmployeeGroup, null=True)
class User(models.Model):
_job = models.ForegnKey(Company, null=True)
employee_type = models.ManyToManyField(
EmployeeType,
null=True,
)
def __setattr__(self, attrname, val):
if attrname == 'job' and isinstance(val, Company):
val.employee_groups.add(self.employee_type.employee_group)
self._job = val
Now when you will set to some user a new job, that in company wich hire them will added a new employee_group.
After that you can easy get all employee_groups in any company you want by simple request:
employee = Users.objects.get(some_user_pk)
empoyer = Company.objects.get(some_company_pk)
employee.job = employer
print(empoyer.employee_groups) # will contain employee_group of new employee
I have a non-ForeignKey ID from an external system that acts as my join key. I'd like to do Django ORM style queries using this ID.
My desired query is:
results = MyModel.objects.filter(level='M', children__name__contains='SOMETHING')
My model looks like this:
class MyModel(BaseModel):
LEVELS = (
('I', 'Instance'),
('M', 'Master'),
('J', 'Joined')
)
level = models.CharField(max_length=2, choices=LEVELS, default='I')
parent = models.ForeignKey('self', blank=True, null=True, related_name='children', on_delete=models.SET_NULL )
master_id = models.CharField(max_length=200)
name = models.CharField(max_length=300, blank=True, null=True)
This works fine with parent as a field, but parent is redundant with the master_id field: master_id indicates which children belong to which master node. I'd like to get rid of parent (primarily because the dataset is fairly large and setting the parent IDs when importing data takes a long time).
The SQL equivalent of what I'm looking for is:
SELECT
DISTINCT( s_m.master_id )
FROM
mytable s_m JOIN
mytable s_i ON
s_i.level = 'I' and s_m.level='M' AND s_i.master_id == s_m.master_id
WHERE
s_i.name like '%SOMETHING%';
I believe there's a way to use Manager or QuerySet to enable clean querying of children (in this case, the children's names) within the Django ORM framework, but I can't figure out how. Any pointers would be appreciated.
Can you try something like this?
table1.objects.filter(master_id__in=table2.objects.filter(level='I').values_list(master_id,flat=True),level='M',name__contains='SOMETHING').values_list(master_id).distinct()
I have a seializer class
class StudentJournalSerializer(serializers.ModelSerializer):
class Meta:
model = StudentJournalModel
fields = (
'value',
'date',
'discipline',
'para_number',
'student',
'is_module'
)
Which I cant get to work as I need it to.
I want it to display not the pk values for ForeignKey fields but actual field values plus this class should work for post methods as well.
Usually I used to add:
student = serializers.CharField(
source='student.username'
)
discipline = serializers.CharField(
source='discipline.discipline'
)
para_number = serializers.CharField(
source='para_number.para_position'
)
However it only works with GET read_only=True
But I need to use it during post requests from client app to create new objects in database so it obviously wont work. I read that i need to write the .create() method to handle such case but I dont really understand how it works and what i need to overwrite there, so I would really appreciate if someone can explain how it should be and why.
Attaching code for the model as well:
class StudentJournalModel(models.Model):
value = models.CharField(
max_length=55,
blank=True,
null=True,
verbose_name="Value",
default=''
)
date = models.DateField(
verbose_name="Date"
)
discipline = models.ForeignKey(
'department.Disciplines',
verbose_name="Discipline"
)
para_number = models.ForeignKey(
'department.ParaTime',
verbose_name="Class #"
)
student = models.ForeignKey(
User,
verbose_name="Student"
)
is_module = models.BooleanField(
verbose_name="Module value"
)
def __unicode__(self):
return u"%s, %s, %s" % (self.date, self.discipline, self.student.get_full_name())
You are looking for SlugRelatedField.
Note that you need to make sure that the slug field has a unique constraint.
If you want to expose all the fields for the model it's enough to just say:
class StudentJournalSerializer(serializers.ModelSerializer):
class Meta:
model = StudentJournalModel
depth = 1
From docs: The depth option should be set to an integer value that indicates the depth of relationships that should be traversed before reverting to a flat representation.
This will work to GET the nested resource also, for POST you will need to send the ids for related fields, it should be enough to create the new object. The student, discipline and para_number should already exist in database, if any of them should also be created then you need to write .create() method yourself see this nice little exemple from docs
The basic idea is that I want to track training and have a roster for each training session. I would like to also track who entered each person in the roster hence a table rather than just an M2M to the Member model within Training.
So, here is what I currently have:
class Training( models.Model ):
name = models.CharField( max_length=100, db_index=True )
date = models.DateField( db_index=True )
roster = models.ManyToManyField( Member, through='TrainingRoster' )
class TrainingRoster( models.Model ):
training = models.ForeignKey( Training )
member = models.ForeignKey( Member )
## auto info
entered_by = models.ForeignKey( Member, related_name='training_roster_entered_by' )
entered_on = models.DateTimeField( auto_now_add = True )
The problem is that django doesn't like the "roster=models.m2m( Member, through='TrainingRoster') as there are two fields in TrainingRoster with a ForeignKey of Member. I understand why it is unhappy, but is there not a way to specify something like: through='TrainingRoster.member'. That doesn't work, but it seems like it should.
[I will admit that I am wondering if the "entered_by" and "entered_on" fields are the best for these models. I want to track who is entering each piece of information but possible a log table might be better than having the two extra fields in the TrainingRoster table. But that is a whole separate question. Though would make this question easier. :-) ]