Django: getting access to related fields in other modles - python

I have the below
class Chore(models.Model):
title = models.CharField(max_length=300)
reward = models.PositiveIntegerField( default= 1)
def __str__(self):
return self.title
class Child(models.Model):
created_by = models.ForeignKey(User,on_delete=models.PROTECT, blank = True, null = True)
name = models.CharField(max_length=300)
age = models.DecimalField (max_digits=2, decimal_places=1, default=3.0, blank = True )
class ChildChore(models.Model):
child = models.ForeignKey(Child, on_delete=models.PROTECT)
chore = models.ForeignKey(Chore, on_delete=models.PROTECT)
points = models.PositiveIntegerField( default= 0, blank = True, null = True)
Form:
class ProgressForm(ModelForm):
class Meta:
model = ChildChore
exclude = ()
ProgressFormSet = modelformset_factory(ChildChore, form=ProgressForm, extra=0, can_delete=[False])
html page:
{{ formset.management_form }}
{% for form in formset %}
{{form.chore}} # will return the title
{{form.child}} # will return the child name
{{form.points}}
View:
def ProgressCreate(request, cid):
template_name = 'chore/progress_form.html'
if request.method == 'GET':
formset = ProgressFormSet(queryset=ChildChore.objects.filter(child=cid))
elif request.method == 'POST':
formset = ProgressFormSet(request.POST)
if formset.is_valid():
for form in formset:
# only save if name is points
if form.cleaned_data.get('points'):
form.save()
return redirect('children-list')
else:
print (formset.errors)
return render(request, 'chore/progress_form.html', {'formset': formset})
my question here is how to get the other models fields in the template? For example, I want the "reward" from Chore Model and I need the "age" form Child.
something like {{form.child.age}} will not work and creating a function in the child model to return the age didn't work as well.
I tried to make

Related

How to update an item in django without creating a new item?

I'm trying to update an item(book) present in the database, Even though I've added instance so that a new item is not created but instead the item is only updated, but unfortunately it's not working as it is supposed to, Instead of updating the item, a new item is being created, am I missing something in here?
models.py
class Book(models.Model):
book_name = models.CharField(max_length= 100)
author_name = models.CharField(max_length=100)
publisher = models.CharField(max_length=100)
published_on = models.DateTimeField(blank=True, null=True)
Language = models.CharField(max_length=100)
image = models.ImageField(blank = True, upload_to='images/')
created = models.DateTimeField(auto_now_add = True)
def __str__(self):
return self.book_name
#property
def imageURL(self):
try:
url = self.image.url
except:
url = " "
return url
views.py
def book_register(request):
if request.method == 'POST':
form = BookForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('/')
else :
return render(request, 'crud_operation/book_form.html', {'form': form})
else:
form = BookForm()
context = {'form':form}
return render(request,'crud_operation/book_form.html',context)
def book_update(request,pk):
book = Book.objects.get(id=pk)
form = BookForm(instance = book)
if request.method == 'POST':
form = BookForm(request.POST, request.FILES, instance=book)
if form.is_valid():
form.save()
return redirect('/')
context = {'form':form}
return render(request, 'crud_operation/book_form.html',context)
urls.py
urlpatterns = [
path('create-book/',views.book_register, name = 'books'),
path('update-book/<int:pk>/',views.book_update, name = 'book_update'),
]
forms.py
class BookForm(ModelForm):
class Meta:
model = Book
fields = '__all__'
widgets = {
'published_on': DateInput(attrs={'type': 'date'})
}

Refer to current model when submitting forms django

views.py
form = StudentTaskForm(request.POST or None, request.FILES or None)
if form.is_valid():
form.instance.user = User.objects.get(id=request.user.id)
obj = form.save(commit=False)
obj.student = request.user
obj.todo = qs
obj.level = instance
obj.save()
ImageFormSet = modelformset_factory(Images,
form=ImageForm,min_num=0,
max_num=3, validate_min=True,extra=3)
if request.method == 'POST':
formset = ImageFormSet(request.POST, request.FILES,
queryset=Images.objects.none())
if formset.is_valid():
for form in formset.cleaned_data:
try:
image = form['image']
Images.objects.create(post=todo[0],image=image)
except KeyError:
pass
return redirect('student:dashboard')
else:
formset = ImageFormSet(queryset=Images.objects.none())
forms.py:
class StudentTaskForm(forms.ModelForm):
title = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-
control',' type': "text",'placeholder':'Enter Title'}))
content = forms.CharField(widget=SummernoteWidget())
class Meta:
model = Task
fields = [
'title',
'content',
]
widgets = {
'content': SummernoteWidget(),
}
def clean_object(self):
instance = self.instance
class ImageForm(forms.ModelForm):
image = forms.ImageField(label='Image')
class Meta:
model = Images
fields = ('image', )
Im using formsets for saving images.A task may contain a maximum of three images.
In models.py i have :
class Task(models.Model):
level = models.ForeignKey(Level, on_delete=models.CASCADE)
todo = models.ForeignKey(ToDo,
on_delete=models.CASCADE,related_name='todo')
student = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=150)
content = models.TextField()
timestamp = models.TimeField(auto_now=True)
datestamp = models.DateField( auto_now=True)
like =
models.ManyToManyField(User,related_name='user_likes',blank=True)
is_verified=models.BooleanField(default=False,blank=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('student:dashboard')
objects = PostManager()
#property
def comments(self):
instance = self
qs = Comment.objects.filter_by_instance(instance)
return qs
#property
def get_content_type(self):
instance = self
content_type =
ContentType.objects.get_for_model(instance.__class__)
return content_type
class Images(models.Model):
post = models.ForeignKey(Task,
default=None,on_delete=models.CASCADE)
image = models.ImageField(verbose_name='Image',blank=True)
def __str__(self):
return self.post.title
Im creating another model to save the images that belong to a particular task
How do i target the post when submitting the form.?
Images.objects.create(post=todo[0],image=image)
Im confused what to put in post=? .By the definition of the model the post is a foreign key to a task object .i want this task object to be the model for currently submitting form model from the StudentTaskForm
You should be using an inline formset, which will handle this for you.
ImageFormSet = inlineformset_factory(Task, Images,
form=ImageForm, min_num=0,
max_num=3, validate_min=True, extra=3)
formset = ImageFormSet(request.POST, request.FILES, instance=obj)
if formset.is_valid():
formset.save()
Here there's no need to iterate at all.

How to display records belongs only for user instead for all users

I have a little problem. I want to to display in list only records belongs to user whos add it. In my app when I'am login as 'user' and I want to add new records incude records from list, I see all records in db from ForeignKey. How to make it correctly?
In 'Strona www' when I expand the list I see all records, not only records added by user personally.
My view for it is:
#login_required
def new_keyword(request):
if request.method == "POST":
new_keyword = KeywordForm(request.POST)
if new_keyword.is_valid():
new_keyword=new_keyword.save(commit=False)
new_keyword.user = request.user
new_keyword.save()
messages.success(request, 'Pomyślnie dodano słowo kluczowe')
return render(request, 'konto/new_keyword_ok.html')
else:
new_keyword = WebsiteForm()
return render(request, 'konto/new_keyword.html', {'new_keyword':new_keyword})
in forms.py I have:
class KeywordForm(forms.ModelForm):
class Meta:
model = Keyword
fields = ('www', 'keyword')
models.py
class Keyword(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="Użytkownik")
www = models.ForeignKey(Website, on_delete=models.CASCADE, verbose_name="Strona www")
keyword = models.CharField(max_length=250, verbose_name="Słowo kluczowe", unique=False)
urls.py
path('new-keyword/', new_keyword, name='new_keyword'),
and html for display the form is:
{% if request.user.is_authenticated %}
<form action="." method="post">
{{ new_keyword.as_p }}
{% csrf_token %}
<p><input type="submit" value="Dodaj nowe słowo kluczowe" ></p>
Powrót do monitoringu
</form>
{% endif %}
EDIT:
models.py
from django.db import models
from django.contrib.auth.models import User
class Website(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="Użytkownik")
website = models.CharField(max_length=250,verbose_name='Strona www', unique=False)
class Meta:
verbose_name = 'Strona www'
verbose_name_plural = 'Strony www'
def __str__(self):
return self.website
class Keyword(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="Użytkownik")
www = models.ForeignKey(Website, on_delete=models.CASCADE, verbose_name="Strona www")
keyword = models.CharField(max_length=250, verbose_name="Słowo kluczowe", unique=False)
class Meta:
verbose_name = 'Słowo kluczowe'
verbose_name_plural = 'Słowa kluczowe'
def __str__(self):
return self.keyword
Pass the request.user to your form and use the inverse relation user.website_set :
forms :
class KeywordForm(forms.ModelForm):
class Meta:
model = Keyword
fields = ('www', 'keyword')
def __init__(self, user, *args, **kwargs):
super(KeywordForm, self).__init__(*args, **kwargs)
self.fields['www'].queryset = user.website_set.all()
self.user = user
def save(self, commit=True):
instance = super(KeywordForm, self).save(commit=False)
instance.user = self.user
if commit:
instance.save()
return instance
views:
#login_required
def new_keyword(request):
if request.method == "POST":
new_keyword = KeywordForm(request.user, request.POST)
if new_keyword.is_valid():
new_keyword.save()
messages.success(request, 'Pomyślnie dodano słowo kluczowe')
# DONT DO THIS ! REDIRECT INSTEAD
return render(request, 'konto/new_keyword_ok.html')
else:
new_keyword = KeywordForm(request.user)
return render(request, 'konto/new_keyword.html', {'new_keyword':new_keyword})
As a side note: after a successful POST you want to redirect (even if to the same url). This avoids a lot of troubles (and duplicate submissions) when a user reloads the page
Just pass the user id to your form and then set a queryset for your field
like this:
How to pass the user to your form:
in your view:
#login_required
def new_keyword(request):
if request.method == "POST":
#### Notice this part.
new_keyword = KeywordForm(data=request.POST, u=request.user)
if new_keyword.is_valid():
new_keyword=new_keyword.save(commit=False)
new_keyword.user = request.user
new_keyword.save()
messages.success(request, 'Pomyślnie dodano słowo kluczowe')
return render(request, 'konto/new_keyword_ok.html')
else:
# Notice this part.
new_keyword = WebsiteForm(data=None, u=request.user)
return render(request, 'konto/new_keyword.html', {'new_keyword':new_keyword})
How to retrieve the user in form and set the queryset:
forms.py:
class KeywordForm(forms.ModelForm):
class Meta:
model = Keyword
fields = ('www', 'keyword')
def __init__(self, current_user, *args, **kwargs):
self.u = kwargs.pop("u") ## The user you just passed.
super(KeywordForm, self).__init__(*args, **kwargs)
self.fields['www'].queryset = Website.objects.filter(user=self.u) ## Setting the queryset.

Working with multiple related django modelforms

I'm trying to get two forms derived from related models to display on the same page and save together.
This is my models.py:
class userinfo(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, primary_key= True,
on_delete=models.CASCADE)
name = models.CharField(max_length = 200, blank = True)
email = models.EmailField(max_length= 300, default = 'Null')
phone = models.CharField(max_length= 10, default = 'Null')
def __unicode__(self):
return self.user
class seller_event(models.Model):
event_id = models.AutoField(primary_key=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
title = models.CharField(max_length = 300, default = 'Null')
description = models.CharField(max_length = 300, default = 'Null')
location = models.CharField(max_length= 300, default = 'Null')
cash_payment = models.BooleanField(default=False)
paytm_payment = models.BooleanField(default=False)
def __unicode__(self):
return str(self.event_id) + str(self.title)
As you can see, the user (saved as user_id in my sqlite3 db) is the foreign key from the userinfo table. I'm trying to get multiple events listed for each seller.
forms.py:
class Userinfo(forms.ModelForm):
class Meta:
model = userinfo
exclude = {'user'}
class Seller_event(forms.ModelForm):
class Meta:
model = seller_event
exclude = {'user'}
views.py:
def newlist(request):
if request.user.is_authenticated():
user = request.user
print user
if request.method == 'POST':
userinfo_form = Userinfo(request.POST, instance = user.userinfo)
seller_event_form = Seller_event(request.POST, instance =
user.seller_event)
if userinfo_form.is_valid() and seller_event_form.is_valid():
userinfo_form.save()
seller_event_form.save()
return redirect('/home')
else:
userinfo_form = Userinfo()
seller_event_form = Seller_event()
return render(request, 'home/newlist.html', {'userinfo_form':
userinfo_form, 'seller_event_form': seller_event_form })
html:
{% load static %}
<form action="/home/newlist/" method="post">
{% csrf_token %}
{{ userinfo_form.as_p }}
{{ seller_event_form.as_p }}
<input type="submit">
</form>
I suspect the problem is that while for userinfo, the user pk is a onetoone, there is no issue with identifying the model. However, this is not the case for seller_event. Here it is a fk. So, how do I get it to accept the user as an fk?
Thanks in advance!
After form validation, save your form without committing, then set your foreign key and save.
if userinfo_form.is_valid() and seller_event_form.is_valid():
userinfo_form.save()
new_seller_event = seller_event_form.save(commit=False)
new_seller_event.user = user
new_seller_event.save()
return redirect('/home')

how to fill out the form initial data?

please help with the form is first loaded in the form fields display data from a database.
models:
class Status(models.Model):
status = models.CharField(max_length=40, blank=False, )
#classmethod
def get_status_list(self):
status_list = list()
status_list.append(('', 'no selected'))
for values_ins in Status.objects.all().values_list('id', 'status'):
status_list.append(values_ins)
return status_list
class UserProfile(User):
name1 = models.CharField('Имя', max_length=30, blank=True, null=True, )
name2 = models.CharField('Отчество', max_length=30, blank=True, null=True, )
status = models.ForeignKey(Status, verbose_name='Статус', blank=True, null=True, )
forms:
class PersonalDataForm(forms.ModelForm):
status = forms.ChoiceField(widget=forms.Select, choices=Status.get_status_list(),label='Статус',required=False, )
class Meta:
model = UserProfile
fields = ('name1', 'name2', )
views:
#login_required
def personal_data_page(request):
entry_user_profile = UserProfile.objects.get(user_ptr_id=request.user.id)
form = PersonalDataForm(instance=entry_user_profile)
if request.method == 'POST':
form = PersonalDataForm(request.POST, instance=entry_user_profile)
if form.is_valid():
form.save()
entry_user_profile.save()
return HttpResponseRedirect('/userprofile/personal_data_page_changed/')
t = loader.get_template('personal_data_page.html')
c = RequestContext(request, {'form': form,})
return HttpResponse(t.render(c))
template:
{{ form.name1 }}
{{ form.name2 }}
{{ form.status }}
the problem is that the form is first loaded the user can see the data from the database in the fields: "name1", "name2". but does not see the data from the database in the "status" (it shows a point "no selected", but must show the item corresponding to the value in the table UserProfile.status_id)
I need to when you first load the form in the "status" indicated a certain value. please help
You are using a model form but then you have override the status field in your form so you need to set the initial value manually by overriding the __init__ method (one way of doing it, it can be done in other ways also):
class PersonalDataForm(forms.ModelForm):
status = forms.ChoiceField(
widget=forms.Select,
choices=Status.get_status_list(),
label='Статус',
required=False
)
def __init__(self, *args, **kwargs):
super(PersonalDataForm, self).__init__(*args, **kwargs)
self.fields["status"].initial = self.instance.status.id # this line sets the value
class Meta:
model = UserProfile
fields = ('name1', 'name2', )
Following are some improvements for your other pieces of code.
Simplified get_status_list method:
#classmethod
def get_status_list(self):
status_list = Status.objects.all().values_list('id', 'status')
status_list.insert(0, ('', 'no selected'))
return status_list
You don't need to have entry_user_profile.save() after form.save(). The form save method will return the updated the profile instance by default. So following two lines:
form.save()
entry_user_profile.save()
Simplified as:
entry_user_profile = form.save()

Categories

Resources