Django, how to modify output of a related model serizlizer? - sql

I have a model which has several ForeignKeys:
class Employee(models.Model):
company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name="staff", null=False, blank=False)
user = models.ForeignKey(to=User, blank=False, null=False, on_delete=models.CASCADE)
roles = models.ManyToManyField(to=Role, blank=False)
How do I use a fields from Company and User in Employee model? I need them for __str__.

you can access these as attributes of the self.company or self.user, since self.company will return a Company object.
You thus can work with:
class Employee(models.Model):
company = models.ForeignKey(
Company,
on_delete=models.CASCADE,
related_name='staff'
)
user = models.ForeignKey(
User,
on_delete=models.CASCADE
)
roles = models.ManyToManyField(Role)
def __str__(self):
return f'{self.company} {self.user.username}'
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

Related

How to avoid duplicates when Prefetch m2m related objects with soft deletable models?

I want to get list of accounts with not deleted relations.
Models
class User(models.Model):
accounts = models.ManyToManyField(
to='billing.Account',
through='custom_auth.UserAccount',
through_fields=('user', 'account'),
related_name='users',
)
deleted = models.DateTimeField(
verbose_name=_('Deleted at'),
blank=True,
null=True,
db_index=True
)
objects = UserQuerySet.as_manager()
class UserAccount(models.Model):
user = models.ForeignKey(
to='custom_auth.User',
on_delete=models.CASCADE)
account = models.ForeignKey(
to='billing.Account',
blank=True,
null=True,
on_delete=models.CASCADE)
deleted = models.DateTimeField(
verbose_name=_('Deleted at'),
blank=True,
null=True,
db_index=True
)
class Account(models.Model):
_users = models.ManyToManyField('custom_auth.User', blank=True)
User Manager
class UserQuerySet(models.QuerySet):
def prefetch_accounts_for_api(self, request):
accounts_qs = Account.objects.all()
user_account_qs = UserAccount.objects.filter(
user=request.user,
account_id=OuterRef('pk'),
deleted__isnull=True
)[:1]
accounts_qs = accounts_qs.filter(
useraccount=user_account_qs
)
return self.prefetch_related(Prefetch(
'accounts',
queryset=accounts_qs,
to_attr='enabled_accounts'
))
The problem is that when there are two rows in useraccount table (1 deleted and 1 not deleted) and i execute the query:
User.objects.all().prefetch_accounts_for_api(request)
User have duplicate of not deleted account relation in enabled_accounts attribute.
How can i get only one actual account in enabled_accounts?
Using PostgreSQL and Django 3.1.7

from sql to Django... i don't understand

i have 3 models (protocollo_pratica, protocollo_soggetto and protocollo_anaprat) and i need extract for each protocollo_pratica a list of soggetti with a specific "tipo" but i don't understand how to make this in django views. Thank you so much for your help.
`
this is my models.py :
`
from django.db import models
# Create your models here.
class Soggetto(models.Model):
cognome = models.CharField(max_length=100, null=True)
nome = models.CharField(max_length=100, null=True)
comune_nascita = models.CharField(max_length=100, null=True)
data_nascita = models.DateField(null=True)
codice_fiscale = models.CharField(max_length=16, null=True)
def __str__(self):
"""String for representing the MyModelName object (in Admin site etc.)."""
return self.nome
class Pratica(models.Model):
oggetto = models.CharField(max_length=100)
anagrafe = models.ManyToManyField(Soggetto,
through='Anaprat',
through_fields=('pratica', 'soggetto'),)
def __str__(self):
"""String for representing the MyModelName object (in Admin site etc.)."""
return self.oggetto
class Anaprat(models.Model):
tipo=models.CharField( max_length=50)
pratica=models.ForeignKey("Pratica", related_name='pratiche', on_delete=models.CASCADE)
soggetto=models.ForeignKey("Soggetto", related_name='soggetti', on_delete=models.CASCADE)
def __str__(self):
"""String for representing the MyModelName object (in Admin site etc.)."""
return f"{self.tipo, self.soggetto}"`
and this is my sql query :
`SELECT
p.id,
pa.tipo tipo,
ps.cognome,
ps.nome,
ps.data_nascita,
ps.comune_nascita
from
(SELECT
id id
from protocollo_pratica pp ) p,
protocollo_anaprat pa
left JOIN protocollo_soggetto ps
on ps.id=pa.soggetto_id
where p.id=pa.pratica_id`

Edit many-to-many relationship in Flask-SQLAlchemy withg WTForms

I struggle handling a many-to-many relationship (here: users and groups) in a Flask form. My database structure (simplified) looks as follow:
association_user_group = db.Table(
'association_user_group',
db.Column('user_id', db.Integer, db.ForeignKey('user.id')),
db.Column('group_id', db.Integer, db.ForeignKey('group.id'))
)
class Group(db.Model):
__tablename__ = 'group'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(32))
users = db.relationship(
'User',
secondary=association_user_group,
backref=db.backref('groups', lazy='dynamic'))
class User(UserMixin, db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
#property
def group_ids(self):
return [u.id for u in self.groups]
To handle a form that allows an admin to edit the users, I have:
class MultiCheckboxField(SelectMultipleField):
widget = widgets.ListWidget(prefix_label=False)
option_widget = widgets.CheckboxInput()
class UserEditForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
password = PasswordField('Password')
groups = MultiCheckboxField('Groups', coerce=int)
submit = SubmitField('Apply changes')
and the following route:
#bp.route('/user_edit/<int:id>', methods=['GET', 'POST'])
#login_required
#admin_required
def user_edit(id):
user = User.query.get(id)
if request.method == 'GET':
form = UserEditForm(obj=user)
form.groups.data = [grp.id for grp in user.groups]
else:
form = UserEditForm(request.form)
form.groups.choices = [(grp.id, grp.name) for grp in Group.query.all()]
if form.validate_on_submit():
#form.populate_obj(user) <-- does not work
user.username = form.data.username
if form.password.data != '':
user.set_password(form.password.data)
# how do I update the 'groups'?
db.session.add(user)
db.session.commit()
return "Data={}".format(form.data)
The database and form works, but I am unable to copy the groups form content back to the database. Ideally, I'd be able to 'populate back' the form content to the User object, but this fails because of the groups (and I think it might also fail with the password field that is empty if no change is requested).
I then tried to delete all groups from user and re-add the ones I want, but did not figure out a reasonable way to achieve this. I am able to add an group membership with user.groups.append(grp) where grp is the corresponding database object. I also am able to remove a group membership in the same way, but given the group ids this would mean looping through all group ids, retrieving the group object, and then using this object to invoke the remove method, which seems overly complicated.
Overall, I suspect that I attempt to implements all this in a far too awkward way...

Got AttributeError when attempting to get a value for field `email` on serializer `UserSerializer`

Creating Django REST FRamework API. Using Abstract User and email is the default logging parameter using.
AttributeError at /post/
Got AttributeError when attempting to get a value for field email on serializer UserSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the Post instance.
Original exception text was: 'Post' object has no attribute 'email'.
class PostSerializer(ModelSerializer):
category = ReadOnlyField(source='category.name')
author = UserSerializer(source='user.email')
#question = serializers.CharField(source='question.text', read_only=True)
class Meta:
model = Post
fields = '__all__'
class User(AbstractUser):
username = models.CharField("Username", max_length=50, unique=True)
email = models.EmailField("Email Address", max_length=254, unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username', 'first_name', 'last_name']
class Post(models.Model):
STATUS_CHOICES = (
('draft', 'Draft'),
('published', 'Published'),
)
title = models.CharField(max_length=250)
slug = models.SlugField(max_length=250, unique_for_date='publish')
author = models.ForeignKey(User, related_name='posts', on_delete=models.CASCADE, to_field='email')
category = models.ForeignKey(Category, related_name='categorys', on_delete=models.CASCADE)
body = models.TextField()
image = models.ImageField(upload_to='blog/%Y/%m/%d', blank=True)
publish = models.DateTimeField(default=timezone.now)
rating = models.IntegerField("Rumor Rate", validators=[MaxValueValidator(5), MinValueValidator(0)], default=1, null=True)
created = models.DateTime[![enter image description here][1]][1]Field(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
I suppose you want to return the author's email in the author field when you request the Post endpoint. You may also convert the author field in the serializer to a ReadOnlyField (as category) and specify source='author.email', since your model foreign key is named like that:
class PostSerializer(ModelSerializer):
category = ReadOnlyField(source='category.name')
author = ReadOnlyField(source='author.email')
# ...

Django Model Design (Big model vs multiple)

I have a question about designing my models. Suppose I have a following model:
class Comment(models.Model):
who = models.ForeignKey(User, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
text = models.CharField(max_length=1000)
likes = models.IntegerField(default=0)
parent_comment = models.ForeignKey('self', on_delete=models.CASCADE, null=True, related_name='child_comments')
Now I would like models for multiple topics (ShoppingList, Games,...). I came to two possible solutions and need help with deciding more suitable one.
1) Make Comment abstract and extend it for every new model wanted.
class ShoppingListComment(Comment):
shopping_list = models.ForeignKey(ShoppingList, related_name='shopping_comments', on_delete=models.CASCADE)
I could then query this game comments with something like:
ShoppingListComment.objects.all()
2) Add extra nullable Foreing keys directly to comment:
class BigCommentModel(models.Model):
who = models.ForeignKey(User, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
text = models.CharField(max_length=1000)
likes = models.IntegerField(default=0)
parent_comment = models.ForeignKey('self', on_delete=models.CASCADE, null=True, related_name='child_comments')
shopping_list = models.ForeignKey(ShoppingList, related_name='shopping_comments', on_delete=models.CASCADE, null=True),
game = models.ForeignKey(Game, related_name='game_comments', on_delete=models.CASCADE, null=True)
I could then query this game comments with something lile:
BigCommentModel.objects.filter(game__isnull=False)