Django "change" model from another application - django

I'm going to create some application with Django admin interface, with plugins in mind.
For example I have some user class in billing application:
class User(models.Model):
ContractNum = models.PositiveIntegerField(unique=True, blank=True, null=True )
LastName = models.CharField(max_length=50,)
and I have cmdb application, which has another model:
class Switch(models.Model):
Name = models.CharField(max_length=50, )
Manufacturer = models.CharField(max_length=50, )
Model = models.CharField(max_length=50, )
I would like to somehow virtually add to User model in billing application fields to have something like:
class User(models.Model):
ContractNum = models.PositiveIntegerField(unique=True, blank=True, null=True )
LastName = models.CharField(max_length=50,)
Switch = models.ForeignKey(Switch, related_name='SwitchUsers')
when I'm installed application cmdb, without any change in billing application dynamically.
I've read about abstract class, but I don't see the way how to achieve what I want with it, because it will add User into cmdb application, and I want something keep billing without changes as main application for the project.

Create a third model with has a OneToOne to User and a ForeignKey to Switch.

Related

Possibility of linking a user/profile to a previously-created name linked to a model via a foreign key

I have a Django application where registered users can add, through an input form, details of performances of their music ensemble. This application also has a a section for composers, where they add their own composition. I'm using a custom user model, with profiles linked to user accounts:
class User(AbstractBaseUser):
email = models.EmailField(verbose_name="email", unique=True, max_length=255)
first_name = models.CharField(max_length=30, blank=True, null=True)
[...]
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
[...]
This is my 'composition' model:
class Composition(models.Model):
title = models.CharField(max_length=120) # max_length = required
composer = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
[...]
And this is my 'performance' model. The performance information links to the piece performed (performed):
class Performance(models.Model):
performed = models.ManyToManyField(Composition, blank=True)
[...]
So far, so good. Now, I'd like the performers to be able to add pieces by composers who are not (yet) registered to the website. Let's say that the performer performed a piece by John Lennon and is adding information about that performance. So, the performer will need to be able to add both John Lennon, his composition, and link the two.
The most important bit is: if the ghost of John Lennon tomorrow wants to register to the website, the administrator of the website will need to be able to easily link the compositions already added by the performers to John Lennon's newly created account. Is that possible? I suppose the key to solving this issue is changing composer = models.ForeignKey(settings.AUTH_USER_MODEL... with something else, i.e. using a intermediary model. Any suggestion will be greatly appreciated.
There are different ways to do this, you should choose one based on your taste:
When you know that the actual user is registered, just delete the old, fake user referred in the composition and replace it with the actual user.
Create a new model named something like Artist and change the composer relationship to refer to the Artist model. then, link the Artist model to the actual user with a nullable foreign key.

How to extend Django groups for dynamic user types?

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

Django choices with model objects

Yes, this is an assignment, and yes, I've spent some time on it and now I need help.
My task has two models, Server and Client and they are in 1-N relationship. as noted below
# models.py
class Server(models.Model):
name = models.CharField(max_length=255, unique=True, null=False, blank=False)
maximum_clients = models.IntegerField(default=1,null=False, blank=False)
class Client(models.Model):
name = models.CharField(max_length=255, unique=True, null=False, blank=False)
active = models.BooleanField(default=True)
server = models.ForeignKey(Server)
I have created a form with ModelForm which allows me to create a new client on a given server, but the prerequisite of the task is to only offer servers which have free capacity (their maximum_clients is less than actual clients) so this is what I did
#forms.py
from django.db.models import Count
qs = Server.objects.annotate(Count('client'))
server_choices = []
for server in qs:
if server.client__count < server.maximum_clients:
server_choices.append((server,server))
class ClientForm(forms.ModelForm):
name = forms.CharField(label='Client name')
server = forms.ChoiceField(choices=server_choices)
class Meta:
model = Client
fields = ('name','server',)
This approach populates the select with the right servers based on a precondition that I mentioned. However, saving this form produces an error like Cannot assign "u'fifty'": "Client.server" must be a "Server" instance. Fifty is the name of the server with maximum_clients = 50
There is a similar form on admin screens which I also modified to show only available servers and saving there produces the same error.
This is not the right approach. Apart from the error you are seeing, you will also find that your server_choices only update when you restart the webserver, rather than doing so whenever the Server objects themselves change.
You have a foreign key, and need to select from a subset of the related objects. The correct field for that is a ModelChoiceField; this takes a queryset which you can filter in the definition. Since your filter depends on a field in the same model, you need to use an F object.
class ClientForm(forms.ModelForm):
name = forms.CharField(label='Client name')
server = forms.ModelChoiceField(
queryset=Server.objects.annotate(client_count=Count('client')).filter(client_count__lt=F('maximum_clients')
)

Relationships with Django and rest framework add replace fields

My user object with rest framework has an avatar_id and a cover_id. But Instead of displaying that to the API, I want it to be the actual avatar URL and cover URL already.
My User model:
avatar_id = models.IntegerField()
cover_id = models.IntegerField()
My UserAvatar model:
id = models.IntegerField(primary_key=True)
user_id = models.IntegerField()
file_id = models.IntegerField()
My Files model:
id = models.IntegerField(primary_key=True)
filename = models.CharField(max_length=255)
Same concept with UserCover.
How do I remove the avatar_id from the results of /users/ and add a avatar field with the actual avatar filename?
I'm not sure I understand your question correctly, but here what I think the problems are. Reading your question, I assumed that you are a beginner, so I answered as such. Sorry if it's not the case.
You don't need to add the id fields, it's done automatically by Django because all tables need a primary key. You define a PK only when you need to name it something else than 'id'.
You should really read the Django tutorial which explains how to define models. User.cover_id and UserAvatar.file_id should be defined as ForeignKey. If you don't know what a foreign key is, then stop playing with Django and read a database tutorial before.
There's already a model and a set of classes to manage your users in Django. You should use them. For example, a "user profile" is the right way to extend the user model.
If you force the users to choose one avatar in a set of predefined avatars, then what you want to do is ok. If the users can choose any avatar (upload), then you should use OneToOneField or put it directly in the user model (or profile).
I don't know what is a UserCover, but here's what your models could look like:
from django.contrib.auth.models import User
class UserProfile(models.Model):
# Link to Django normal User (name, email, pass)
user = models.ForeignKey(User, unique=True)
# Any information that a user needs, like cover, wathever that is, age, sexe, etc.
avatar = models.CharField(max_length=255)
Or like this if a will be reused often :
class Avatar(models.Model):
# name = ...
# description = ...
path = models.CharField(max_length=255)
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
avatar = models.ForeignKey(Avatar, unique=True)
# other data

Displaying both father and son in a RESTful API?

I'm trying to use the Django REST framework to generate json I'll be able to use later on my website. The thing is, I have Users, who have one-to-many projects, and these projects have one-to-many tasks.
The thing is, I'd like to display a list of my users, then when I access the details of an user, I can see his projects. Then, when I check the details of a project, I can see the tasks of this project. Now, my models are defined like this :
class SimpleUser(AbstractBaseUser):
username = models.TextField(max_length=40, unique=True)
firstname = models.TextField(max_length=40)
lastname = models.TextField(max_length=40)
class Project(models.Model):
user = models.ForeignKey(SimpleUser, null=True)
name = models.TextField(max_length=255)
class Task(models.Model):
project = models.ForeignKey(Project, related_name='task_project')
title = models.TextField(max_length=255)
And when I'm trying to display for example the Projects linked to an User, I have an error no matter what I try (I'm pretty sure this is because what I do is wrong), and the error is "SimpleUser has no attribute Project". Which is logical. But I really don't know how to do this, can someone help me ?
You should use project_set to access SimpleUsers Projects.
user = SimpleUser.objects.get(id=1)
projects = user.project_set.all()
Or define a specific name for the manager with related_name=:
user = models.ForeignKey(SimpleUser, null=True, related_name='projects')
Then you can access users projects via projects:
projects = user.projects.all()
Try specifying explicitly the related_name for the relationship:
class Project(models.Model):
user = models.ForeignKey(SimpleUser, null=True, related_name='projects')
name = models.TextField(max_length=255)