Model field to automatically store total club members - python

I am building an app for a charity club that has many different users each belonging to a single club. I want to automatically increment the 'total_members' field of the class 'Club_Chapter' each time a user registers their account for a particular school (hence the User foreign key 'chapter').
models.py
class Club_Chapter(models.Model):
club_id = models.IntegerField(primary_key=True)
school_name = models.CharField(max_length=30)
state = models.CharField(max_length=4)
total_members = models.IntegerField(null = False, default = 0)
def __str__(self):
return self.school_name
# User Model
class User(AbstractUser):
username = None
email = models.EmailField(_('email address'), unique=True)
USERNAME_FIELD = 'email'
chapter = models.ForeignKey('Club_Chapter',on_delete=models.PROTECT)
ranking = models.CharField(default='member', max_length=20)
REQUIRED_FIELDS = []
objects = UserManager()
forms.py
class SignUpForm(UserCreationForm):
first_name = forms.CharField(max_length=30, required=True)
last_name = forms.CharField(max_length=30, required=True)
email = forms.EmailField(max_length=254, required=True)
chapter = models.ForeignKey('Club_Chapter',on_delete=models.PROTECT)
password2 = None
# below is my failed attempt to solve this problem
Club_Chapter.objects.update(total_members=F('total_members') + 1)
class Meta:
model = User
fields = ('first_name', 'last_name',
'chapter','email', 'password1',)
I know this seams like a trivial problem but I have been searching everywhere with no luck. Any input will be greatly appreciated for the solution to this problem will help greatly with other aspects of this project.

I'm not sure what it's important to have an auto-incrementing total_members fields. Django has super powerful querying capabilities. For instance, this would work perfectly:
total_members = User.objects.filter(chapter__school_name="My Cool School").count()
Notice I used the double underscore after chapter to reference a particular field in the foreign key.
Hope this helps.

Related

Better way to fetch related object, in Self Refrencing ManyToMany Relationship?

I am working on a small application containing models CustomUser and PollQuestion. CustomUser having ManyToMany relation between itself and a CustomUser can have multiple PollsQuestion as well so there is Foreign Key relation between them.
An authenticated user is only able to see polls raised by users he is following, to full-fill this requirement i have written following view**:-**
Actually this is not view this is an utility method returning the polls to original view.
def all_polls_utils(request):
following = request.user.get_all_followings().values_list("id")
user_id = [id[0] for id in following]
all_polls = PollQuestion.objects.none()
for u_id in user_id:
user = CustomUser.objects.get(id=u_id)
polls = user.poll_questions.all()
all_polls = all_polls | polls
return all_polls
Main Question:- Is there in better way to do the same?
Any suggestion will be highly appretiated
I am posting the models bellow:-
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class CustomUser(AbstractUser):
email = models.EmailField(max_length=250, null=False)
name = models.CharField(max_length=50, null=False)
username = models.CharField(max_length=50, null=False, unique=True)
password = models.CharField(max_length=15, null=False)
user = models.ManyToManyField('self', through='Relationship', symmetrical=False, related_name='related_to')
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['name', 'email']
def get_all_polls(self):
pass
def create_relationship(self, person):
status, obj = Relationship.objects.get_or_create(
to_person=person,
from_person=self,
)
return status
def remove_relationship(self, person):
Relationship.objects.filter(
from_person=self,
to_person=person
).delete()
return 'dummy_value'
def get_all_followings(self):
return self.user.all()
class Relationship(models.Model):
from_person = models.ForeignKey(CustomUser, related_name='from_people', on_delete=models.CASCADE)
to_person = models.ForeignKey(CustomUser, related_name='to_person', on_delete=models.CASCADE)
And PollQuestion:-
class PollQuestion(models.Model):
user = models.ForeignKey(CustomUser, null=True, on_delete=models.CASCADE, related_name="poll_questions")
# Other fields
Note:- You can also suggest me a better title for this post?
Thanks in advance,
Hope to here from you soon.
Simply
def all_polls_utils(request):
all_polls_by_followed = PollQuestion.objects.filter(
user__in=request.user.get_all_followings()
)
As an aside, you should probably rename the user many-to-many in CustomUser to e.g. followed_users (with a related name followers).

radio buttons incorrectly written to the database in django

I've a registration form, where user must chose one of 2 options.
Django renders all correctly, django admin also have it ok, but db records all possible choices as value.
forms.py
class UserRegisterForm(UserCreationForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username', 'email','password1','password2']
class UserProfileForm(forms.ModelForm):
terms_compliance = forms.BooleanField(label=mark_safe('I agree with terms and conditions '))
class Meta:
model = UserProfile
widgets = {'role': forms.RadioSelect}
fields = ('role','terms_compliance')
def __init__(self):
self.fields['terms_compliance'].initial = True
models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
role_choices = [('publisher','Publisher'), ('advertiser','Advertiser')]
role = models.CharField(max_length=15, choices=role_choices, default=None)
terms_compliance = models.BooleanField()
def __str__(self):
return self.user.username
In new instance (which is user.userprofile.role_choices) I need advertiser or publisher, but all I have is: [('publisher','Publisher'), ('advertiser','Advertiser')]
If you want to provide choices in a Database Field. Do like this:
class UserProfile(models.Model):
class RoleChoice(ChoiceEnum):
PUBLISHER = 'Издатель'
ADVERTISER = 'Рекламодатель'
user = models.OneToOneField(User, on_delete=models.CASCADE)
role = models.CharField(max_length=15, choices=RoleChoice.choices(), default=None)
terms_compliance = models.BooleanField()
def __str__(self):
return self.user
In Views.py, populate the DB like this.
For example:
...
choice = request.query_params.get('choice') or UserProfile.RoleChoice.PUBLISHER.value
...
For more details read from here: https://django-mysql.readthedocs.io/en/latest/model_fields/enum_field.html

Django Rest Framework: How to do nested serialization with ContentType

I have an application that has a Company, and this company can have 0 or multiple addresses. Companies are not the only 'models' that could have addresses. To achieve this, I use ContentTypes.
models.py
class Company(models.Model):
''' models a company in the system. '''
number = models.CharField(_('Number'), unique=True, max_length=20)
name = models.CharField(_('Name'), max_length=100)
active = models.BooleanField(_('Active'), default=True)
def _get_addresses(self):
'''
'''
contentType = ContentType.objects.get(
model=self.__class__.__name__.lower()
)
try:
addresses = Address.objects.get(
actor_type=contentType, actor_id=self.id
)
except Address.DoesNotExist:
addresses = []
return addresses
addresses = property(_get_addresses)
class Address(Auditable):
''' models an address '''
actor_type = models.ForeignKey(ContentType)
actor_id = models.PositiveIntegerField()
actor_object = fields.GenericForeignKey('actor_type', 'actor_id')
_type = models.CharField(
_('Address Type'),
max_length=10,
choices=sorted(TYPES.items())
)
active = models.BooleanField(default=True)
address1 = models.CharField(_('Address 1'), max_length=50)
address2 = models.CharField(
_('Address 2'),
max_length=50,
...
This way, I could also have a Profile model and I could link multiple addresses to a Profile. However, my problem comes when I tried to implement the Serializers.
Serializers.py
class AddressSerializer(serializers.ModelSerializer):
class Meta:
model = Address
fields = (
'_type',
'address1',
'address2',
'address3',
'country',
'state',
'city'
)
class CompanySerializer(serializers.ModelSerializer):
addresses = AddressSerializer(required=False, many=True)
class Meta:
model = Company
fields = (
'number',
'name',
'addresses'
)
This implementation gives me this error. It says that it cannot iterate through Address model (which makes sense), but I'm not sure how to make my addresses iterable.
I would need to perform CRUD operations not only on the Company, but also on the nested Addresses.
Any suggestions/ideas on how to go about this?
The following database schema is just a suggestion for the scenario prevailed.
class Details(models.Model):
official_name = models.CharField(....)
is_company = models.BooleanField(default=False)
#something like that....
class Address(models.Model):
owner = models.ForeignKey(Details, related_name='addresses')
is_company = models.BooleanField(default=False)
building_name = .....
building_no = ......
locality = ......
#some other fields.....
class Company(models.Model):
details = models.OneToOneField(Details, related_name='company')
name = models.CharField(...)
number = .....
is_active = ........
class Profile(models.Model):
details = models.OneToOneField(Details, related_name='profile')
name = models.CharField(....)
.........
Here, each Company table and Profile table has a one to one relationship with a Details table. The Details table is related to the Address table with a Foreign key in it. So, each Company or Profile can have multiple addresses through the Details table.
So, the queries would be like,
For accessing addresses from the Company or Profile instances,
company.details.addresses.all()#or
profile.details.addresses.all()
For reverse queries are simple as the tables contains the respective fields in them, for a given address the owner would be, address.owner.profile or address.owner.company which could be determined by a flag in the respective tables.
I know, designing the database like this is somewhat tiring. But, this does helps in serialization of the data into a better format.
Serializers can be as follows,
class DetailsSerializer(ModelSerializer):
addresses = AddressSerializer(source='addresses', many=True)
class Meta:
model = Details
fields = ('addresses',)
class AddressSerializer(ModelSerializer):
class Meta:
model = Address
fields = '__all__'
class CompanySerializer(ModelSerializer):
details = DetailsSerializer()
class Meta:
model = Company
fields = ('details', .........)
class ProfileSerializer(ModelSerializer):
details = DetailsSerializer()
class Meta:
model = Profile
fields = ('details', .........)

How to I pass values into dependent models with Factory Boy in Django?

Im' working on an open source django web app, and I'm looking to use Factory Boy to help me setting up models for some tests, but after a few hours reading the docs and looking at examples, I think I need to accept defeat and ask here.
I have a Customer model which looks a bit like this:
class Customer(models.Model):
def save(self, *args, **kwargs):
if not self.full_name:
raise ValidationError('The full_name field is required')
super(Customer, self).save(*args, **kwargs)
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
related_name='customer',
null=True
)
created = models.DateTimeField()
created_in_billing_week = models.CharField(max_length=9)
full_name = models.CharField(max_length=255)
nickname = models.CharField(max_length=30)
mobile = models.CharField(max_length=30, default='', blank=True)
gocardless_current_mandate = models.OneToOneField(
BillingGoCardlessMandate,
on_delete=models.SET_NULL,
related_name='in_use_for_customer',
null=True,
)
I am also using the standard Django User Model, from django.contrib.auth.
Here's my factory code:
class UserFactory(DjangoModelFactory):
class Meta:
model = get_user_model()
class CustomerFactory(DjangoModelFactory):
class Meta:
model = models.Customer
full_name = fake.name()
nickname = factory.LazyAttribute(lambda obj: obj.full_name.split(' ')[0])
created = factory.LazyFunction(timezone.now)
created_in_billing_week = factory.LazyAttribute(lambda obj: str(get_billing_week(obj.created)))
mobile = fake.phone_number()
user = factory.SubFactory(UserFactory, username=nickname,
email="{}#example.com".format(nickname))
In my case, I want to be able to generate a customer like so
CustomerFactory(fullname="Joe Bloggs")
And have the corresponding user generated, with the correct username, and email address.
Right now, I'm getting this error:
AttributeError: The parameter full_name is unknown. Evaluated attributes are {'email': '<factory.declarations.LazyAttribute object at 0x111d999e8>#example.com'}, definitions are {'email': '<factory.declarations.LazyAttribute object at 0x111d999e8>#example.com', 'username': <DeclarationWrapper for <factory.declarations.LazyAttribute object at 0x111d999e8>>}.
I think this is because I'm relying on a lazy attribute here in the customer, which isn't called before the user factory is created.
How should I be doing this if I want to be able to use a factory to create the Customer model instance, with a corresponding user as described above?
For what it's worth the full model is visible here on the github repo
In that case, the best way is to pick values from the customer declarations:
class CustomerFactory(DjangoModelFactory):
class Meta:
model = models.Customer
full_name = factory.Faker('name')
nickname = factory.LazyAttribute(lambda obj: obj.full_name.split(' ')[0])
created = factory.LazyFunction(timezone.now)
created_in_billing_week = factory.LazyAttribute(lambda obj: str(get_billing_week(obj.created)))
mobile = factory.Faker('phone_number')
user = factory.SubFactory(
UserFactory,
username=factory.SelfAttribute('..nickname'),
email=factory.LazyAttribute(lambda u: "{}#example.com".format(u.username)))
)
Also, use factory.Faker('field_name') to get random values for each instance: the line fullname = fake.name() in the class declaration is equivalent to:
DEFAULT_FULL_NAME = fake.name()
class CustomerFactory(DjangoModelFactory):
full_name = DEFAULT_FULL_NAME
class Meta:
model = models.customer
Whereas factory.Faker('name') is equivalent to:
class CustomerFactory(DjangoModelFactory):
full_name = factory.LazyFunction(fake.name)
class Meta:
model = models.customer
i.e fake.name() will provide a different name to each model built with this factory.

Userena - Extending the Profile model further upto two different models

What i am trying to achieve is that i want to extend the profile model further to either teacher or student. In the signup form I added a choice field where user select whether he is teacher or student. Below is my model structure.
class Profile(UserenaLanguageBaseProfile):
""" Default profile """
GENDER_CHOICES = (
(1, _('Male')),
(2, _('Female')),
)
user = models.OneToOneField(User,
unique=True,
verbose_name=_('user'),
related_name='profile')
gender = models.PositiveSmallIntegerField(_('gender'),
choices=GENDER_CHOICES,
blank=True,
null=True)
class Teacher(Profile):
profile = models.OneToOneField(Profile,
unique=True,
verbose_name=_('profile'),
related_name='teacher')
home_address = models.CharField(_('home_address'), max_length=255, blank=True)
home_phone = models.CharField(_('home_phone'), max_length=30, blank=True)
cell_phone = models.CharField(_('cell_phone'), max_length=30, blank=True)
experience = models.IntegerField(default = 0)
summary = models.TextField(_('summary'), max_length=500, blank=True)
class Student(Profile):
profile = models.OneToOneField(Profile,
unique=True,
verbose_name=_('profile'),
related_name='student')
grade = models.CharField(_('grade'), max_length=50, blank=True)
I am overriding the signup save method as:
def save(self):
new_user = super(SignupFormExtra, self).save()
new_user.first_name = self.cleaned_data['first_name']
new_user.last_name = self.cleaned_data['last_name']
new_user.save()
if self.cleaned_data['teacher_or_student'] == 'teacher':
teacher = Teacher(profile = new_user.get_profile())
teacher.save()
elif self.cleaned_data['teacher_or_student'] == 'student':
student = Student(profile = new_user.get_profile())
student.save()
return new_user
When teacher.save() or student.save() method is called it raises an integrity error that "(1048, "Column 'user_id' cannot be null")" but i am not creating a new user instance here i am trying to assign the newly created profile_id to teacher or student model. I am doing in the wrong way?? what should I do?
As the error says you can't create a Student or Teacher without user as you've defined it as a non nullable field.
Make sure you're passing your class the new_user you've defined..
# ...
new_user.save()
if self.cleaned_data['teacher_or_student'] == 'teacher':
teacher = Teacher(profile = new_user.get_profile(), user=new_user)
teacher.save()
elif self.cleaned_data['teacher_or_student'] == 'student':
student = Student(profile = new_user.get_profile(), user=new_user)
student.save()
I might be wrong, but why do you subclass your models from Profile model (so you have a "user" field in it already), and right after you have a "profile" OneToOneField field for Profile model again?

Categories

Resources