I am working on a blog type website.
where students will ask question and teacher will answer it.
I put two user role
Teacher and student
class User(AbstractBaseUser, PermissionsMixin):
USER_TYPE = (
('None', 'None'),
('Student', 'Student'),
('Teacher', 'Teacher'),
)
user_type = models.CharField(choices=USER_TYPE, default='Student', max_length=50)
This is the user model where the role is defined.
Post model for submitting the question and Comment model for answering it.
In the Post model I put on field is_answered and as default put it False
class add_question(models.Model):
created_by = models.ForeignKey(User, blank=False, on_delete=models.CASCADE)
is_answered = models.BooleanField(default=False)
And the answer model [which is comment one] is referred to as the foreign key of the question
class submit_answer(models.Model):
question_id = models.ForeignKey(
add_question, blank=False, on_delete=models.CASCADE)
created_by = models.ForeignKey(User, blank=False, on_delete=models.CASCADE)
Both question and answer model contains created_by field and ForeignKey of User and answer model contains another ForeignKey of question.
so I need when the teacher role will put a comment the is_answered Field in add_question model should turn as true.
as student also can comment on his/er question so I need on condition to apply.
I am using serializer as I need API of this
So, should I modify on my views.py or serializers.py and how should I do that!
It would be great help if someone guide me how to do it?
Thanks and let me know if any other information needed.
The best practice would be adding such codition in save() method of models.Model. You should name your Model classes differently, like Question and Answer, because after creating, if you want to edit or something, it would be weird if you have to search for submit_answer, right? And as #wjh18 said, use CamelCase in classes.
Also question_id is usually bad idea, better think of question, because it will lead directly to the whole object, not its id.
class Answer(models.Model):
question = models.ForeignKey(Question, blank=False, on_delete=models.CASCADE)
created_by = models.ForeignKey(User, blank=False, on_delete=models.CASCADE)
def save(self, *args, **kwargs):
if self.created_by.user_type == 'Teacher' and not self.question.is_answered:
self.question.is_answered = True
self.question.save()
super().save(*args, **kwargs)
Related
Need help getting a better grip with 1) accessing and counting foreign key items in Django and 2) counting foreign key item that is created through Django's built-in User:
#1)
The answer here suggests using Django's annotate function: eg. questions = Question.objects.annotate(number_of_answers=Count('answer')) (question being a foreign key item to answer). But it is not clear to me where this line belongs to (in the case of the example, question or answer), or which part of an app-models.py or views.py. I tested in both models.py and views.py
Here are my models (only the essential lines here):
class Post(models.Model):
post = models.TextField(max_length=1000)
author = models.ForeignKey(User, related_name="author", on_delete=models.CASCADE)
class Comment(models.Model):
comment = models.TextField(max_length=1000)
commenter = models.ForeignKey(User, on_delete=models.CASCADE)
post_connected = models.ForeignKey(Post, related_name='posts', on_delete=models.CASCADE, default=None, null=True)
#2) How does one count a foreign key item created using Django's built-in model, User?
Should I have created a model called "Author" first?
I want to tell you something about related_name:
related_name help you access objects of another model for example
class Post(models.Model):
post = models.TextField(max_length=1000)
author = models.ForeignKey(User, related_name="posts", on_delete=models.CASCADE)
class Comment(models.Model):
comment = models.TextField(max_length=1000)
commenter = models.ForeignKey(User, on_delete=models.CASCADE)
post_connected = models.ForeignKey(Post, related_name='comments', on_delete=models.CASCADE, default=None, null=True)
with modelobject.related_name.all() get objects of another models in this example userobject.posts.all() you can get all post related to user object.
and with post.comments.all() can get all comments related to the post.
I am having a problem regarding adding entry to a many to many relation field. I have the models as follows
class Address(models.Model):
id = models.AutoField(primary_key=True)
country = CountryField(blank_label='(select country)', blank=False, null=False, verbose_name='Country')
state = models.CharField(
max_length=50,
choices=STATE_CHOICES,
verbose_name='State',
blank=False,
null=False
)
...
class Volunteer(models.Model):
userID = models.OneToOneField(User, on_delete=models.CASCADE, to_field='id', primary_key=True, related_name='volunteer')
identificationNumber = models.CharField(max_length=50, unique=True, blank=False, null=False, verbose_name='Identification Number')
currentAddress = models.ManyToManyField(Address, related_name='volunteerCurrentAddress', verbose_name='Current Address', blank=False)
permanentAddress = models.ManyToManyField(Address, related_name='volunteerPermanentAddress', verbose_name='Permanent Address', blank=False)
...
def save(self, *args, **kwargs):
self.slug = self.userID.username
super(Volunteer, self).save(*args, **kwargs)
class TemporaryVolunteer(Volunteer):
pass
And in the views, I get both the currentAddress and permanentAddress fields as a ManyToManyRelatedManager. They are temporaryVolunteer.currentAddress and temporaryVolunteer.permanentAddress. I use these to create a new Volunteer instance as
volunteer = Volunteer(...)
volunteer.save()
volunteer.currentAddress.add(temporaryVolunteer.currentAddress.all()[0])
volunteer.permanentAddress.add(temporaryVolunteer.permanentAddress.all()[0])
volunteer.save()
But when I do print(volunteer.currentAddress.all()) or print(volunteer.permanentAddress.all()), it returns an empty queryset. I also checked the admin site for confirmation and there are no entries of address on the volunteer instance.
Is there any way the entries can be added with this approach?
The problem was in the design of the database. I had used a new class TemporaryVolunteer in order to store unverified accounts and later moved to the Volunteer class after they were verfied. As I had inherited TemporaryVolunteer from Volunteer class, the way Django handles Many to many relationships without making duplicates (Source: https://docs.djangoproject.com/en/3.0/topics/db/examples/many_to_many/) led to the problem of no entries being added in the corresponding Volunteer class.
Initially, I checked this by copying all the members of Volunteer to TemporaryVolunteer. After verifying that it worked, I changed the database design as this is a bad approach and kept a boolean value isVerified in the Volunteer class and removed the TemporaryVolunteer class entirely.
I have the following abstract class:
class UserStamp(models.Model):
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True,
related_name='%(app_label)s_%(class)s_created_by', on_delete=models.CASCADE)
updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True,
related_name='%(app_label)s_%(class)s_updated_by', on_delete=models.CASCADE)
class Meta:
abstract = True
The Account Model inherits from it:
class Account(UserStamp):
pass
And I have a User Model with a FK to
class User(AbstractBaseUser,PermissionsMixin, UserStamp):
account = models.ForeignKey(Account, blank=True, null=True, related_name='owner',on_delete=models.CASCADE)
I have the following error when I migrate:
django.db.migrations.exceptions.CircularDependencyError:
The circular error I think appears because:
Account is calling User by inheriting created_by, updated_by from UserStamp, so points to User, and User points with Account FK back to Account.
If I use:
account = models.ForeignKey('accounts.Account', blank=True, null=True, related_name='owner',on_delete=models.CASCADE)
the issues is not solved.
My problem has (2 issues), but I decided to split the initial question in 2 questions to be more clear(I think they can be solved separately):
Second part here (how to set created_by, updated_by)
You can set a default user by modifying the save() method of your model:
class UserStamp(models.Model):
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True,
...
def save(self, *args, **kwargs):
if not self.created_by:
super_users = settings.AUTH_USER_MODEL.objects.filter(
is_superuser=True).order_by('date_joined')
first_user = super_users[0]
self.created_by = first_user
super().save(*args, **kwargs)
But I don't think that's what's causing your migration error. You may want to try a OneToOneField
I'm kind of new to django. While writing the models for an app I'm doing, I felt like I was doing something wrong because of all the foreign keys I was using for a single model (ticket model to be specific)
My thinking at the beginning was that I wanted each Ticket to keep the information on it's creator, what team this ticket is assigned to and the comments made on the ticket. And other information that don't need foreign keys. Am I doing this right ? I dont know why, but I feel like there is a better way.
class Team(models.Model):
name = models.CharField(max_length=200)
description = models.CharField(max_length=2000)
members = models.ManyToManyField(User, through='Team_members')
def __str__(self):
return self.name
class Ticket(models.Model):
name = models.CharField(max_length=200)
creator = models.ForeignKey(User, on_delete=models.CASCADE)
team = models.ForeignKey(Team, on_delete=models.CASCADE)
comments = models.ForeignKey(Comment, on_delete=models.CASCADE)
#worker = models.ForeignKey(User, on_delete=models.CASCADE) *to be finished
description = models.CharField(max_length=500)
status = models.BooleanField(default=False)
date_opened = models.DateTimeField('date opened')
date_closed = models.DateTimeField('date closed',null=True, blank=True)
def __str__(self):
return self.name
class Team_member:
team = models.ForeignKey(Team)
user = models.ForeignKey(User)
date = models.DateTimeField('date joined')
class Comment:
text = models.CharField(max_length=2000)
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.text
Your question actually has very little (like, nothing?) to do with Django.
Please read about database normalization, it should answer your questions.
Regarding your models, I could notice the following:
Your models look quite good. Don't be afraid of foreign keys, they are the main reason you use a relational database :)
I assume, User might be a member of a Team (service?) or somebody who opens a ticket. If so: when you will have worker foreign key, you most likely won't need team in the Ticket model. It will be redundant, as worker would have a relation to Team.
Nitpicking: Team_member is not pythonic, a PEP8 compliant version would be TeamMember
I have a model in which I have:
ipv4_address = models.IPAddressField(verbose_name=_('ipv4 address'), blank=True, null=True, unique=True, default=None)
ipv6_address = models.GenericIPAddressField(protocol='IPv6', verbose_name=_('ipv6 address'), blank=True, null=True, unique=True, default=None)
And gives me the troubles described here: https://code.djangoproject.com/ticket/4136
How can I solve this?
I was thinking of making a custom GenericIPAddressField which inserts NULL into the database instead of an empty string. What do you think? Are there other possible solutions?
Your suggestion of a custom field GenericIPAddressField is probably the most DRY solution. akonsu's suggestion in the comments above to override the save method is a good one. A quick hack I have used is to clean the value to None in your model form.
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
def clean_ipv6_address(self):
"""
Return None instead of empty string
"""
return self.cleaned_data.get('ipv6_address') or None
If you edit the model through the django admin, use your model form there
#!admin.py
class MyModelAdmin(admin.ModelAdmin):
form = MyModelForm
admin.site.register(MyModel, MyModelAdmin)