I am new to Django and would like to create a platform where teachers can create student profiles and assign reports to the specific student.
Therefore, I have made two models (Student & Report) and two CreateViews. When creating a report, the teacher has the option to select a student from a choice field. This I have done using ModelChoiceField. However, when submitting the report model, the students name is not saved to that model. How can I accomplish this?
Ultimately I would like to have a profile to each student showing their info and reports attached to their model.
models.py
class Student(models.Model):
author = models.ForeignKey(User, null=True, on_delete=models.CASCADE)
name = models.CharField(max_length=30, default="")
age = models.CharField(max_length=2, default="", blank=True)
class Report(models.Model):
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, null=True, on_delete=models.CASCADE)
title = models.CharField(max_length=30)
forms.py
class StudentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(StudentForm, self).__init__(*args, **kwargs)
class Meta:
model = Student
fields = ['name','age',]
class ReportForm(forms.ModelForm):
student = forms.ModelChoiceField(queryset=Student.name, initial=0)
class Meta:
model = Report
fields = ['title','file','player',]
views.py
class StudentCreate(LoginRequiredMixin, CreateView):
model = Student
template_name = 'student_create.html'
form_class = forms.StudentForm
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class ReportCreate(LoginRequiredMixin, CreateView):
model = Report
template_name = 'report_create.html'
form_class = forms.ReportForm
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
Currently your Report model is like so:
class Report(models.Model):
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, null=True, on_delete=models.CASCADE)
title = models.CharField(max_length=30)
The basic concept behind a model is that it is an abstraction / design of how we want the database table for some entity to look like. So the fields we add in a model are columns / attributes in our database table. If we want to store some data we need to have some column for it (or in terms of models a field).
Your model Report has no field for storing some student (in essence a foreign key to a Student, a foreign key is basically a pointer to a particular entry of another table). So you should add that to your model:
class Report(models.Model):
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, null=True, on_delete=models.CASCADE)
title = models.CharField(max_length=30)
student = models.ForeignKey(Student, on_delete=models.CASCADE, related_name="reports")
Related
I'm developing a project in Django. I have several registered companies, and all models are based on the company.
#models.py
class Company(models.Model):
name = models.CharField(max_length=100)
country = models.CharField(max_length=100)
class XUser(User):
phone = models.CharField(max_length=20, null=True, blank=True)
card = models.CharField(max_length=20, null=False, blank=True)
company = models.ForeignKey(Company, on_delete=models.CASCADE, blank=True, null=True)
def __str__(self):
return self.nombre
class Book(models.Model):
user = models.ForeignKey(XUser, on_delete=models.CASCADE)
company = models.ForeignKey(Company, on_delete=models.CASCADE)
name = models.CharField(max_length=30)
class Paragraph(models.Model):
company = models.ForeignKey(Company, on_delete=models.CASCADE)
book = models.ForeignKey(Book, on_delete=models.CASCADE)
text = models.CharField(max_length=300)
Now in my admin I define that every user can only see the books of his company.
#admin.py
#admin.register(Book)
class BookAdmin(admin.ModelAdmin):
def get_queryset(self, request):
qs = super().get_queryset(request)
# return a filtered queryset
return qs.filter(company=request.user.company)
My question is this: When I try to create a Paragraph instance in the django management form, it shows me all the books and I want it to only show me the ones that belong to the user's Company. Any ideas?
I think you can achieve that by overriding the get_form method to change the queryset of company:
admin.py:
#admin.register(Paragraph)
class ParagraphAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
form = super().get_form(request, obj=None, **kwargs)
form.base_fields['book'].queryset = Book.objects.filter(company=request.user.company)
return form
Ive spent a fair bit of time searching on this subject without finding some real up to date answers. I'm trying to create a form that creates a db entry. The basic idea is this:
Many events can have many people
So, the struggle here is that the user needs to create an event where the user can select all the people that attend. Each person that attends though, has certain things that also needs to be tracked per event. See the model below:
models.py
from django.db import models
from django.contrib.auth.models import User[]
class PersonRole(models.Model):
role = models.CharField(max_length=20, choices=ROLE_CHOICES, unique=True)
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return self.role
class PersonClass(models.Model):
name = models.CharField(max_length=100, choices=CLASS_CHOICES, unique=True)
color = models.CharField(max_length=6, choices=COLOR_CHOICES, unique=True)
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return self.name
class Person(models.Model):
name = models.CharField(max_length=100, unique=True)
personclass = models.ForeignKey(PersonClass, on_delete=models.CASCADE, blank=True, null=True)
personrole = models.ForeignKey(PersonRole, on_delete=models.CASCADE, blank=True, null=True)
value = models.IntegerField(default=0)
reliability = models.IntegerField(default=0)
last_item = models.DateField(auto_now=False, blank=True, null=True)
last_event_attended = models.DateField(auto_now=False, blank=True, null=True)
last_manager_attended = models.DateField(auto_now=False, blank=True, null=True)
item_received = models.BooleanField(default=False)
note = models.TextField(null=True, blank=True)
core_attendee = models.BooleanField(default=False)
enabled = models.BooleanField(default=True)
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return self.name
class Location(models.Model):
name = models.CharField(max_length=100, unique=True)
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return self.name
class Boss(models.Model):
name = models.CharField(max_length=100, unique=True)
location = models.ForeignKey(Location, on_delete=models.CASCADE)
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return self.name
class Raid(models.Model):
date = models.DateTimeField(auto_now_add=True)
boss = models.ForeignKey(Boss, on_delete=models.CASCADE, null=True, blank=True)
success = models.BooleanField()
attendees = models.ManyToManyField(Person)
created_by = models.ForeignKey(User,
related_name="raids", blank=True, null=True,
on_delete=models.SET_NULL)
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return str(self.date)
I've started down the path of just trying to use the generic in-built create\update\delete views and ran into this:
ValueError: 'roster.Person' has no ForeignKey to 'roster.Raid'.
forms.py
class RaidGenericCreateModelForm(forms.ModelForm):
class Meta:
model = Person
exclude = ()
RaidPersonFormSet = inlineformset_factory(Raid, Person, fields=['name', 'personclass', 'personrole', 'item_received'], extra=1, can_delete=False)
views.py
class RaidCreate(CreateView):
model = Raid
template_name = 'roster/raid_create.html'
form_class = RaidGenericCreateModelForm
success_url = None
def get(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
person_form = RaidPersonFormSet
return self.render_to_response(
self.get_context_data(form=form,
person_form=person_form
)
)
There are 9-year old posts that say you cannot use inlineformset_factory with many to many fields. So my question here is, what are my options? What is the best way to go about simply creating an Event (referred to as Raid in the model) and at the same time selecting the people from the roster (referred to as Person in the model) and changing the options those people have associated to them for that event?
As an example of what I am trying to accomplish here:
Event 1
-Person A (selected, item_received=True)
-Person B (selected, item_received=False)
-Person C (selected, item_received=False)
-Person D (not selected, item_received=False)
Event 2
-Person A (selected, item_received=False)
-Person B (not selected, item_received=False)
-Person C (selected, item_received=True)
-Person D (selected, item_received=False)
Where the list of persons is showing all persons and some of the persons fields from the Person model.
The alternate thing you can do is use DjangoRestFramework for this purpose.
Using rest you can first send persons data to frontend then in frontend you can create Event and add person details for each event,and in last post all that data using javascript.Try it,it will surely work.
I am trying to build a Survey App. I have defined a model for Survey, Questions, Responses as below.
What I want to do is create a Survey (in admin, which I can do) then display this Survey as a page which users can fill in.
I'm using Class Based Views for the first time and I'm stuck with how to render the Survey form along with the associated Questions and allow input of Answers.
In Admin, I've created a Survey and successfully added Questions to it. FormView (and UpdateView, tried as part of another SO answer) allows me to display the Survey model's 'name' and 'description' attributes - but the questions don't appear.
What do I need to do to make the Survey and it's Questions available in my form (and allow the user to enter a response)?
MODELS
class Survey(models.Model):
name = models.CharField(max_length=400)
description = models.TextField()
def survey_questions(self):
if self.pk:
return Question.objects.filter(survey=self.pk)
else:
return None
def publish(self):
self.published_date = timezone.now()
self.save()
class Question(models.Model):
survey = models.ForeignKey(Survey, on_delete=models.CASCADE)
question = models.TextField()
class Response(models.Model):
member = models.ForeignKey(user_model, on_delete=models.SET_NULL, null=True, blank=True)
survey = models.ForeignKey(Survey, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now_add=True)
class AnswerBase(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
response = models.ForeignKey(Response, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class AnswerText(AnswerBase):
body = models.TextField(blank=True, null=True)
URLS
urlpatterns = [path('survey/<int:pk>/', views.SurveyResponseView.as_view(), name='survey_detail')]
VIEWS
class SurveyResponseView(FormView):
template_name = 'survey/survey_detail.html'
form_class = ResponseForm
FORMS
class ResponseForm(forms.ModelForm):
class Meta():
model = Survey
fields = '__all__'
Okay so the way to do this is to use FormSets. They're a bit of a faff to setup, but shouldn't take you too long. I recommend using this small js library to help.
I also rewrote some of your code to hopefully make life a bit easier for you:
models.py
class SurveyTemplate(models.Model):
name = models.CharField(max_length=400)
description = models.TextField()
# You were missing this I believe
published_date = models.DateTimeField(blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
class Question(models.Model):
# Now you can do SurveyTemplate.questions.all()
survey = models.ForeignKey(Survey, on_delete=models.CASCADE, related_name='questions')
question = models.TextField()
class SurveyResponse(models.Model):
member = models.ForeignKey(user_model, on_delete=models.SET_NULL, null=True, blank=True)
survey = models.ForeignKey(Survey, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now_add=True)
class Answer(models.Model):
# Now you can do Question.answers.all()
question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name='answers')
# Now you can do Response.answers.all()
response = models.ForeignKey(Response, on_delete=models.CASCADE, related_name='answers')
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
views.py
from django.views.generic import CreateView
# CreateView is Django's built in View for creating an object, you would be best to use it
class SurveyResponseView(CreateView):
model = Survey
template = 'survey/survey_detail.html'
form_class = ResponseForm
forms.py
class ResponseForm(forms.ModelForm):
class Meta():
model = Survey
fields = '__all__'
I want to render category field on an article in terms of its name(choice). Currently, it is rendered as an Id. Also, I want to be able to update the article model by entering category name(choice) instead of its Id(I am currently doing this). How can I go about implementing this?
This is what I have so far.
Models
class Category(models.Model):
"""
Model for Category
"""
CATEGORY_CHOICES = (
('Sports', 'Sports'),
('Music', 'Music'),
('Drama', 'Drama'),
('Money', 'Money'),
('Movies', 'Movies'),
('Cars', 'Cars'),
('General', 'General'),
)
name = models.CharField(max_length=30, choices=CATEGORY_CHOICES, default='General',null=False, blank=True)
def __str__(self):
return self.name
class Article(models.Model):
"""
Model for an Article
"""
title = models.CharField(max_length=255, null=False, blank=False)
description = models.TextField(null=False, blank=False)
body = models.TextField(null=False, blank=False,)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
Serializers
class ArticleSerializer(serializers.ModelSerializer):
"""
Serializer for Article.
"""
class Meta:
model = Article
fields = ('title','description','body',''category')
class CategorySerializer(serializers.ModelSerializer):
"""
Serializer for Category.
"""
class Meta:
model = Category
fields = ('name',)
Current output
Expected output
You can change your ArticleSerializer to have the category field as a CharField with a source attribute:
class ArticleSerializer(serializers.ModelSerializer):
category = serializers.CharField(source='category.name')
class Meta:
model = Article
fields = ('title','description','body',''category')
To be able to update the Article's category via the name, you have to make sure that name is a unique field for Category. If yes, then you can use a SlugRelatedField.
category = serializers.SlugRelatedField(slug_field='name')
I have two models Company and Campaign. I need to create a relationship between them. I think my models are fine.
companies/model.py
class Company(models.Model):
class Meta:
verbose_name_plural = "companies"
user = models.ForeignKey(settings.AUTH_USER_MODEL)
title = models.CharField(blank=False, max_length=128, default='')
slug = models.SlugField(blank=True, unique=True)
archived = models.BooleanField(default=False)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
campaigns/models.py
class Campaign(models.Model):
class Meta:
verbose_name_plural = "campaigns"
company = models.ForeignKey('companies.Company', on_delete=models.CASCADE,)
title = models.CharField(blank=False, max_length=128, default='')
slug = models.UUIDField(default=uuid.uuid4, blank=True, editable=False)
def __str__(self):
return str(self.title)
campaigns/forms.py
class CampaignForm(forms.ModelForm):
class Meta:
model = Campaign
fields = ['title','description','archived']
campaigns/views.py
class CampaignCreateView(SubmitBtnMixin, CreateView):
model = Campaign
company = None
form_class = CampaignForm
submit_btn = "Add Campaign"
template_name = "form.html"
campaigns/urls.py
url(r'^campaign/create/$', CampaignCreateView.as_view(), name='campaign-create'),
My question is, when creating a new campaign, where and how do I pick up the Company pk to populate the Campaign model? What is the most secure and best practice for doing this?
I found a solution but would like input on best practices still.
I added this to my CampaignCreateView
def form_valid(self, form):
company = get_object_or_404(Company, id=self.kwargs.get('pk'), user_id=self.request.user.id)
form.instance.company_id = company.id
return super(CampaignCreateView, self).form_valid(form)
and I changed my url to:
url(r'^campaign/(?P<pk>\d+)/create/$', CampaignCreateView.as_view()...
Not sure that I like the pk in the URL since it can be jacked. This is why I am filtering on the userid at the company model to make sure that the data is coming from the owner.
I thought of doing this by registering the company in the session id but I am not convinced that sessions do not present their own problems.