Django CreateView - Display only particular objects in foreignkey field - python

I have a CreateView view that holds a bunch of fields that need to be filled by the user when creating a new contact. Now, I want the user to be able to see and choose only from the categories that they'd created.
This is the model of Category:
class Category(models.Model):
class Meta:
verbose_name = _('category')
verbose_name_plural = _('categories')
name = models.CharField(max_length=100, unique=True)
profile = models.ForeignKey(Profile, on_delete=models.CASCADE)
def __unicode__(self):
return self.name
This is the view:
class ContactCreate(LoginRequiredMixin, generic.edit.CreateView):
model = models.Contact
success_url = reverse_lazy('site:contacts')
fields = ['firstname', 'lastname', 'phone1', 'phone2', 'email', 'city', 'category']
template_name = 'site/contacts.html'
context_object_name = 'all_contacts'
What I need the user to see is a select that has only the categories which include the appropriate profile foreign key associated with them.
I'd be glad to get some help with this. Thank you!

You can override the get_form method of the view and set the queryset of the appropriate field:
class ContactCreate(LoginRequiredMixin, generic.edit.CreateView):
# ...
def get_form(self, *args, **kwargs):
form = super(ContactCreate, self).get_form(*args, **kwargs)
form.fields['categories'].queryset = Category.objects.filter(profile=self.request.user.profile)
return form
This, of course, assumes that your Profile model has a OneToOneField to User with related_name 'profile', otherwise you'd have to adjust the filtering.

Related

Django - get data from form and return to user so they can edit/delete

New to Django and making a restuarant booking system. I have everything going to my database but now i'm trying to return the info back to the user, ideally in a similar form view to what i have, so they can edit or delete the booking.
My code so far is as follows:
models.py:
class Reservations(models.Model):
reservation form categories and attributes
name = models.CharField(max_length=50)
phone_number = models.CharField(validators=[phoneNumberRegex], max_length=16,
unique=True)
email = models.EmailField()
date = models.DateField()
time = models.CharField(choices=time_options, default="12pm", max_length=10)
number_of_party = models.IntegerField(choices=party_size, default=1)
reservation_id = models.UUIDField(primary_key=True, default=uuid.uuid4,
editable=False, max_length=15)
class Meta:
ordering = ['date']
verbose_name = 'Reservation'
verbose_name_plural = 'Reservations'
def __str__(self):
return self.name
Then my form looks like this:
form.py:
class ReservationForm(forms.ModelForm):
class Meta:
model = Reservations
fields = ['name', 'phone_number', 'email', 'date', 'time', 'number_of_party']
widgets = {
'date': DatePickerInput(format='%d-%m-%Y'),
}
Then where I think i'm really getting lost is with the views.
view.py:
class ReservationsFormView(CreateView):
model = Reservations
template_name = "reservations/reservations.html"
form_class = ReservationForm
success_url = "reservation_complete/"
def form_valid(self, form):
return super().form_valid(form)
class EditReservationView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Reservations
template_name = "reservations/edit_reservations.html"
form_class = ReservationForm
success_url = "reservation_complete/"
def test_func(self):
return self.request.user == self.get_object().user
class ReservationCompleteView(CreateView):
template_name = "reservations/reservation_complete.html"
success_url = "/reservation_complete/"
form_class = ReservationForm
model = Reservations
class ReservationAccountView(TemplateView):
template_name = "reservations/reservations_account.html"
class DeleteReservationView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
""" A view to delete an reservation """
model = Reservations
success_url = "/reservations/"
def test_func(self):
return self.request.user == self.get_object().user
So basically if someone could help me extract the data and present back to the user in a html file I would greatly appreciate it.
A lot of the material online isn't using class based views but i start that way so sticking it out.
Sorry for the verbose code.
Thanks in advance
While Django's Edit, Create, etc views are quick and helpful, I find them hard to manipulate, especially if I haven't used them in a while. I recommend getting used to writing your own views, that way you can set whatever action you want the view to perform.
Let's say your URL looks like this - edit/4281ee81-a12a-4a0f-8928-2d9639903bd7/
forms.py
class EditReservationForm(forms.Form):
name = forms.CharField()
email = forms.EmailField()
views.py
class EditReservationView(LoginRequiredMixing, View):
def get(self, request, *args **kwargs):
reservation = Reservations.objects.get(uuid=kwargs['uuid'])
form = EditReservationForm(initial={"name": reservation.name, "email": reservation.email})
return render(request, "reservation/edit-reservation.html", {"form": form})
You now have a bound form to data from a specific reservation. I hope this helps. And more info on initial data here - initial form values

How to pre fill fields in a form when using django-bootstrap-modal-forms

I am using django-bootstrap-modal-forms and it works perfectly as in documentation when using fields form my model. Some of the fields are ForeignKeys and they are displayed properly for user to select a value from database table that is referenced by the key, but instead of that I need to put username of the current user.
I tried to change how the CreateView class handles fields, but with no luck. Probably doing something wrong.
models.py
class userSchoolYear(models.Model):
user_in_school = models.ForeignKey(get_user_model(), null=True, on_delete=models.CASCADE)
school = models.ForeignKey(sifMusicSchool, on_delete=models.CASCADE)
school_year = models.ForeignKey(sifSchoolYear, on_delete=models.CASCADE)
school_year_grade = models.CharField(max_length=4, choices=IZBOR_RAZREDA, default=None, null=True)
user_instrument = models.ForeignKey(instType, on_delete=models.CASCADE, default=None, null=True)
user_profesor = models.ForeignKey(profSchool, on_delete=models.CASCADE, default=None, null=True)
views.py
class SchoolYearCreateView(BSModalCreateView):
template_name = 'school_year.html'
form_class = SchoolYearForm
success_message = 'Success!'
success_url = reverse_lazy('school')
def __init__(self, *args, **kwargs):
self.form_class.user_in_school = 'johnny' ### HERE
print(user.username)
super().__init__(*args, **kwargs)
forms.py
class SchoolYearForm(BSModalForm):
class Meta:
model = userSchoolYear
fields = '__all__'
Thanks to the author Uroš Trstenjak I was able to find a solution. I was wrong trying to set field values from views.py, instead it should be done in forms.py. So, basically I had to write a init for the form and alter fields values. Uroš pointed out that at from level I can get current user from self.request.user and it did work.

CreateView doesn't save related objects

I have two models: Student and Group. A group consists of multiple students and a student can be in only one group. Here are the models:
class Group(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Student(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
section = models.PositiveSmallIntegerField(default=1)
group = models.ForeignKey(
Group, on_delete=models.SET_NULL, null=True, blank=True
)
I am trying to build a form where I can select multiple students to create a group.
class CreateGroupForm(forms.ModelForm):
students = forms.ModelMultipleChoiceField(
required=True,
queryset=Student.objects.all()
)
class Meta:
model = Group
fields = ('students', )
I am using the following view for the form:
class SelectGroup(CreateView):
model = Group
form_class = CreateGroupForm
template_name = 'selection.html'
success_url = reverse_lazy('test')
When I submit the form, it creates a group but the group's student_set is empty. I am guessing this is because I cannot add students to a group without saving the group first. Is there a way for me to modify this view to save the students or should I use something else?
Since students is not a field of the group model, the model form's save does not know what to do with it. You have to override the save method and handle the students field manually:
class CreateGroupForm(forms.ModelForm):
# ...
def save(self, commit=True):
# you have to commit, else the reverse fk has nothing to point to
group = super(CreateGroupForm, self).save(commit=True)
group.student_set.add(*self.cleaned_data['students'])
return group
If you prefer not to remove the non-commit save option on the form, you can also override the form_valid method of the view:
class SelectGroup(CreateView):
# ...
def form_valid(self, form):
self.object.student_set.add(*self.form.cleaned_data['students'])
return super(SelectGroup, self).form_valid(form)

Replace PrimaryKeyRelatedField with another field

I have models which consist in a User model and a Present one. A User can have multiple Present but a Present has a unique User:
My models.py is:
from django.db import models
from django.contrib.auth.models import User
class Present(models.Model):
name = models.CharField(max_length=15)
price = models.FloatField()
link = models.CharField(max_length=15)
isAlreadyBought = models.BooleanField()
user = models.ForeignKey(User, related_name='presents', on_delete=models.CASCADE)
My serializers.py are:
from django.contrib.auth.models import User, Group
from rest_framework import serializers
from blog.models import Present, Location
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
presents = serializers.PrimaryKeyRelatedField(many=True, queryset=Present.objects.all(), required=False)
class Meta:
model = User
fields = ('username', 'password', 'email', 'presents')
def create(self, validated_data):
user = super().create(validated_data)
if 'password' in validated_data:
user.set_password(validated_data['password'])
user.save()
return user
class PresentSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), read_only=False, many=False)
class Meta:
model = Present
fields = ('name', 'link', 'price', 'isAlreadyBought', 'user')
def create(self, validated_data):
return Present.objects.create(**validated_data)
Currently, if I want to get the all the presents associate with a given User, I use the primary key (in views.py):
class PresentsOfUser(viewsets.ModelViewSet):
queryset = Present.objects.all().filter(user=33)
serializer_class = PresentSerializer
However, I would rather use the username field of the User instead of the primary key.
I have tried using a SlugRelatedField but I am not sure this is the right way to achieve my goal:
class PresentSerializer(serializers.ModelSerializer):
user = serializers.SlugRelatedField(queryset=User.objects.all(), slug_field='username', read_only=False, many=False)
class Meta:
model = Present
fields = ('name', 'link', 'price', 'isAlreadyBought', 'user')
def create(self, validated_data):
return Present.objects.create(**validated_data)
And with this modification, I now use the following View to get the user 'Marcel' whose id is 33:
class PresentsOfUser(viewsets.ModelViewSet):
queryset = Present.objects.all().filter(user='Marcel')
serializer_class = PresentSerializer
But in this case, I get:
ValueError: invalid literal for int() with base 10: 'Marcel'
But if I replace user='Marcel' by user=33 (as previously), I get:
[{"name":"Nintendo","link":"fake_link","price":50.8,"isAlreadyBought":true,"user":"Marcel"},{"name":"Gamecube","link":"fake_link","price":50.8,"isAlreadyBought":true,"user":"Marcel"}]
where the user field is now the username and not the user's id.
However, I do not understand why filtering with user='Marcel' fails...
I ended up by overriding the get_querysetmethod while keeping the PrimaryKeyRelatedField in my serializer (with user__username='Marcel'as mishbah suggested):
class PresentsOfUser(viewsets.ModelViewSet):
serializer_class = PresentSerializer
def get_queryset(self):
"""
Optionally restricts the returned purchases to a given user,
by filtering against a `username` query parameter in the URL.
"""
queryset = Present.objects.all()
username = self.kwargs['user']
if username is not None:
queryset = queryset.filter(user__username=username)
if len(queryset) == 0:
raise Http404
return queryset
It also works when replacing PrimaryKeyRelatedField by SlugRelatedField:
user = serializers.SlugRelatedField(queryset=User.objects.all(), slug_field='username', read_only=False, many=False)
and adding to_field='username'in the ForeignKey of my Present model:
user = models.ForeignKey(User, related_name='presents', to_field='username', on_delete=models.CASCADE)
I think your issue is w/ this line:
queryset = Present.objects.all().filter(user='Marcel')
With the assumption Marcel is username with pk => 33
You can't filter using a string, instead something like this:
queryset = Present.objects.all().filter(user__username='Marcel')
Hope that helps.

Cannot post entry specifying specific id if the id is specified as ForeignKey in Django

I have two models, Book and ReadBy as specified in models.py:
class Book(models.Model):
created = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(MyUser)
class ReadBy(models.Model):
created = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(MyUser)
book = models.ForeignKey(Book)
views.py:
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def pre_save(self, obj):
obj.owner = self.request.user
class ReadByViewSet(viewsets.ModelViewSet):
queryset = ReadBy.objects.all()
serializer_class = ReadBySerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def pre_save(self, obj):
obj.owner = self.request.user
serializers.py:
class BookSerializer(serializers.ModelSerializer):
readby = serializers.SerializerMethodField('get_ready_by')
def get_read_by(self, obj):
user = self.context['request'].user
book = obj
return ReadBy.objects.filter(book=book, owner=user).exists()
class Meta:
model = Book
fields = ('id', 'created', 'owner', 'readby')
class ReadBySerializer(serializers.ModelSerializer):
owner = serializers.Field(source='owner.id')
book = serializers.Field(source='book.id')
class Meta:
model = ReadBy
fields = ('id', 'created', 'owner', 'book')
Book is a ForeignKey in ReadBy. The reason for this is that I want to see if the book has been read by defining the readby field in BookSerializer to true or false.
Now when I try to POST a ReadBy item I cannot explicitly set the book_id, even if I try do I still get the same error:
"Column 'book_id' cannot be null"
What I need to know is how I can explicitly specify book_id to be able to "read" a book.
Thanks.
class ReadBy(models.Model):
... snip ...
book = models.ForeignKey(Book, null=True, blank=True)
OK I solved this.
I forgot to define the book field in the ReadBySerializer as PrimaryKeyRelatedField:
book = serializers.PrimaryKeyRelatedField(source='book.id')
In fact it seems like I can remove this line altogether since PrimaryKeyRelatedField is default if nothing is specified.

Categories