I have this model
class Post(models.Model):
title = models.CharField(max_length=100)
title2 = models.CharField( max_length=100)
content = models.TextField(default=timezone.now)
content2 = models.TextField(default=timezone.now)
post_image = models.ImageField(upload_to='post_pics')
post_image2 = models.ImageField(upload_to='post2_pics')
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
Then I have this simple view function that allows me to access each of its field in my HTML:
def home(request):
postings = {
'listings' : Post.objects.all(),
}
return render(request, 'front/front.html', postings)
{% for listings in listings%}
<h1>{{listings.content}}</h1>
{% endfor %}
With this, I'm able to access the content field for every instance of that model and display it
My question is how can I access the content field in my view function and change it. The content field holds a zipcode and I want to use an API to display the city of that zipcode(which I already know how to do) and pass it back to the h1 tag. Each instance holds a unique zipcode so I need it to apply for each instance. How would I approach this?
the simplest way would be to create another variable(from views) which finds the city for a corresponding zipcode and send it through the context dictionary to the template.
OR
Add a model city setting default and Null and later based on the entered pincode you can set value to the city attribute of the model..
If you want to edit the value of the CONTENT to the city name ... then ,
The best way would be to override the save method and set the value there,
models.py :
class Post(models.Model):
...
def save(self, *args, **kwargs):
self.content = API_VALUE_OF_city_name
super(Post, self).save(*args, **kwargs)
if you want to update it from views,
in views.py :
instance_update = Post.objects.filter(id = <pk of Post>).update(content = NEWLY FOUND CITY NAME)
Related
I have the following models:
class Category(models.Model):
label = models.CharField(max_length=40)
description = models.TextField()
class Rating(models.Model):
review = models.ForeignKey(Review, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
rating = models.SmallIntegerField()
class Review(models.Model):
author = models.ForeignKey(User, related_name="%(class)s_author", on_delete=models.CASCADE)
coach = models.ForeignKey(User, related_name="%(class)s_coach", on_delete=models.CASCADE)
comments = models.TextField()
I'd like to create a front-end form which allows a user to review a coach, including a rating for some pre-populated categories.
In my head, the form would look something like:
Coach: _______________ # Selection of all coach users from DB, this works as standard
Category: "Professionalism" # These would be DB entries from the Category model
Rating: _ / 5
Category: "Friendliness"
Rating: _ / 5
Category: "Value"
Rating: _ / 5
Comments:
_________________________________
_________________________________
Submit
I've seen Django Formsets in the documentation but these appear to exist for creating multiple forms from the same model as a single form?
Not looking for a full answer, but if someone could point me in the right direction, it'd be hugely appreciated.
EDIT: Vineet's answer (https://stackoverflow.com/a/65883875/864245) is almost exactly what I'm looking for, but it's for the Admin area, where I need it on the front-end.
Given that the categories are fairly static, you don't want your users to select the categories. The categories themselves should be labels, not fields for your users to select.
You mention in the comment, that the labels will sometimes change. I think there are two questions I would ask before deciding how to proceed here:
Who will update the labels moving forwards (do they have basic coding ability, or are they reliant on using something like the admin).
When the labels change, will their fundamental meaning change or will it just be phrasing
Consideration 1
If the person changing the labels has a basic grasp of Django, and the appropriate permissions (or can ask a dev to make the changes for them) then just hard-coding these 5 things is probably the best way forward at first:
class Review(models.Model):
author = models.ForeignKey(User, related_name="%(class)s_author", on_delete=models.CASCADE)
coach = models.ForeignKey(User, related_name="%(class)s_coach", on_delete=models.CASCADE)
comments = models.TextField()
# Categories go here...
damage = models.SmallIntegerField(
help_text="description can go here",
verbose_name="label goes here"
)
style = models.SmallIntegerField()
control = models.SmallIntegerField()
aggression = models.SmallIntegerField()
This has loads of advantages:
It's one very simple table that is easy to understand, instead of 3 tables with joins.
This will make everything up and down your code-base simpler. It'll make the current situation (managing forms) easier, but it will also make every query, view, template, report, management command, etc. you write easier, moving forwards.
You can edit the labels and descriptions as and when needed with verbose_name and help_text.
If changing the code like this isn't an option though, and the labels have to be set via something like the Django admin-app, then a foreign-key is your only way forward.
Again, you don't really want your users to choose the categories, so I would just dynamically add them as fields, rather than using a formset:
class Category(models.Model):
# the field name will need to be a valid field-name, no space etc.
field_name = models.CharField(max_length=40, unique=True)
label = models.CharField(max_length=40)
description = models.TextField()
class ReviewForm.forms(forms.Form):
coach = forms.ModelChoiceField()
def __init__(self, *args, **kwargs):
return_value = super().__init__(*args, **kwargs)
# Here we dynamically add the category fields
categories = Categories.objects.filter(id__in=[1,2,3,4,5])
for category in categories:
self.fields[category.field_name] = forms.IntegerField(
help_text=category.description,
label=category.label,
required=True,
min_value=1,
max_value=5
)
self.fields['comment'] = forms.CharField(widget=forms.Textarea)
return return_value
Since (I'm assuming) the current user will be the review.author, you are going to need access to request.user and so we should save all your new objects in the view rather than in the form. Your view:
def add_review(request):
if request.method == "POST":
review_form = ReviewForm(request.POST)
if review_form.is_valid():
data = review_form.cleaned_data
# Save the review
review = Review.objects.create(
author=request.user,
coach=data['coach']
comment=data['comment']
)
# Save the ratings
for category in Category.objects.filter(id__in=[1,2,3,4,5]):
Rating.objects.create(
review=review
category=category
rating=data[category.field_name]
)
# potentially return to a confirmation view at this point
if request.method == "GET":
review_form = ReviewForm()
return render(
request,
"add_review.html",
{
"review_form": review_form
}
)
Consideration 2
To see why point 2 (above) is important, imagine the following:
You start off with 4 categories: Damage, Style, Control and Agression.
Your site goes live and some reviews come in. Say Coach Tim McCurrach gets a review with scores of 2,1,3,5 respectively.
Then a few months down the line we realise 'style' isn't a very useful category, so we change the label to 'effectiveness'.
Now Tim McCurrach has a rating of '1' saved against a category that used to have label 'style' but now has label 'effectiveness' which isn't what the author of the review meant at all.
All of your old data is meaningless.
If Style is only ever going to change to things very similar to style we don't need to worry so much about that.
If you do need to change the fundamental nature of labels, I would add an active field to your Category model:
class Category(models.Model):
field_name = models.CharField(max_length=40, unique=True)
label = models.CharField(max_length=40)
description = models.TextField()
active = models.BooleanField()
Then in the code above, instead of Category.objects.filter(id__in=[1,2,3,4,5]) I would write, Category.objects.filter(active=True). To be honest, I think I would do this either way. Hard-coding ids in your code is bad-practice, and very liable to going wrong. This second method is more flexible anyway.
Use this in your app's admin.py file
from django.contrib import admin
from .models import Review, Rating, Category
class RatingInline(admin.TabularInline):
model = Rating
fieldsets = [
('XYZ', {'fields': ('category', 'rating',)})
]
extra = 0
readonly_fields = ('category',)
show_change_link = True
def has_change_permission(self, request, obj=None):
return True
class ReviewAdmin(admin.ModelAdmin):
fields = ('author', 'coach', 'comments')
inlines = [RatingInline]
admin.site.register(Review, ReviewAdmin)
admin.site.register(Rating)
admin.site.register(Category)
You admin page will look like this:
You could "embed" an inline formset into your review form. Then you can call form.save() to save the review and all the associated ratings in one go. Here is a working example:
# forms.py
from django import forms
from . import models
class ReviewForm(forms.ModelForm):
class Meta:
model = models.Review
fields = '__all__'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.ratings = forms.inlineformset_factory(
parent_model=models.Review,
model=models.Rating,
extra=5,
min_num=5,
)(
data=self.data if self.is_bound else None,
files=self.files if self.is_bound else None,
instance=self.instance,
)
def is_valid(self):
return super().is_valid() and self.ratings.is_valid() # AND
def has_changed(self):
return super().has_changed() or self.ratings.has_changed() # OR
def save(self):
review = super().save()
self.ratings.save()
return review
As you can see, the __init__() method sets the attribute self.ratings which you can later recall in your template like this:
<form method="post">
{% csrf_token %}
<div class="review">
{{ form.as_p }}
</div>
<div class="ratings">
{{ form.ratings.management_form }}
{% for rating_form in form.ratings %}
<div class="single_rating">
{{ rating_form.as_p }}
</div>
{% endfor %}
</div>
<button>Save</button>
</form>
Finally, here's how your views.py might look like (using Django's class-based views):
from django.views import generic
from . import models
from . import forms
class ReviewView(generic.UpdateView):
model = models.Review
form_class = forms.ReviewForm
I'm working on my blog page
basically the blog has category for split the same posts,
for this
I made a class for category and made a relationship between the category and my post class like this :
class Category(models.Model):
name = models.CharField(max_length=256)
def __str__(self):
return self.name
class Post(models.Model):
image = models.ImageField(upload_to='Posts_image')
title = models.CharField(max_length=256)
configure_slug = models.CharField(max_length=512)
slug = models.SlugField(max_length=512,null=True,blank=True)
content = HTMLField('Content')
date = models.DateTimeField(auto_now_add=True)
categories = models.ManyToManyField(Category)
tags = TaggableManager()
publish = models.BooleanField(default=False)
def __str__(self):
return self.title
after this I made this function
def blog_category(request):
res_posts = Post.objects.all()
category = request.POST.get('categories__name')
if category:
res_post = res_post.filter(
Q(categories__name__icontains=category)).distinct()
context = {
'posts':res_posts,
}
return render(request, 'Blog/category-result.html', context)
I tell you how its works:
when the users click on the one of category title in the blog page
this functions start work and search how many posts has this category title and list them in the category-result.html
but this function doesn't work correctly
I think this code doesn't work correctly
category = request.POST.get('categories__name')
request.Post can't take the categories__name when user click
Can you help me for this problem???
As you said the user clicks I'm assuming the category title links to a something like http://your.site/your-view?categories__name=selected-category, if that's the case what you need to use is request.GET not request.POST.
I'm very confused about this right now,
so I know when there's a simple code like the below
def text_detail(request ,course_pk, step_pk):
step = get_object_or_404(Text, course_id = course_pk, pk=step_pk)
course_pk and step_pk from the url, and those requests are set equal to course_id and pk here. but what I don't understand is what is course_id and pk here? I mean, course_id is from Course model which is foreignkey to step. so it's self.Course.id so it's course_id. But then, how about the next one pk? shouldn't it be step_id = step_pk? when it's just pk how does django know which pk it is?
Sorry if the question is very confusing, I'm very confused right now.
Edit
class Step(models.Model):
title = models.CharField(max_length=200)
description = models.CharField()
order = models.IntegerField(default=0)
course = models.ForeignKey(Course)
class Meta:
abstract = True
ordering = ['order',]
def __str__(self):
self.title
class Text(Step):
content = models.TextField(blank=True, default="")
Actually the get_or_404() method doing a similar/exact job as below,
try:
return Text.object.get(pk=step_pk,course_id = course_pk)
except Text.DoesNotExist:
raise Http404
You can read the source code of the same here
What is course_id and pk ?
Both are attributes of your Text model, as the name indicates pk is your Primary Key of Text model and course_id is the id/pk of course field which is a FK.
EDIT
Text is inherited from Step model so, it will show properties of usual python class.Hence, the Text model be like this internally (not-exact)
class Text(models.Model):
content = models.TextField(blank=True, default="")
title = models.CharField(max_length=200)
description = models.CharField()
order = models.IntegerField(default=0)
course = models.ForeignKey(Course)
class Meta:
ordering = ['order', ]
def __str__(self):
return self.title
Example
text = Text.objects.get(id=1) # text instance with id=1
text.course_id # will hold the id of "course" instance which is related to the particular "text" instance
URL assignment and all those stuffs are entirely depends on your choice and logic. So If you need to get a Text instance in your view, do as below,
text = get_object_or_404(Text, pk = pk_of_TEXT_instance)
How to create an object for a Django model with a many to many field?
From above question i come to know we can save Many to Many field later only.
models.py
class Store(models.Model):
name = models.CharField(max_length=100)
class Foo(models.Model):
file = models.FileField(upload_to='')
store = models.ManyToManyField(Store, null=True, blank=True)
views.py
new_track.file = request.FILES['file']
new_track.save()
And file uploading working fine then later i modify my code to add store then i am here...
Now i am sure db return id's here. Then i tried with my below code but that's given me error only
x = new_track.id
new = Foo.objects.filter(id=x)
new.store.id = request.POST['store']
new.save()
ok so the error here is 'QuerySet' object has no attribute 'store'
And also i tried with add that's now working either.
So the question is how to save()
the right way of saving objects with manytomany relations would be:
...
new_track.file = request.FILES['file']
new_track.save()
new_store = Store.objects.get(id=int(request.POST['store']))
new_track.store.add(new_store)
As of 2020, here's my approach to saving ManyToMany Field to a given object.
Short Answer
class HostingRequestView(View):
def post(self, request, *args, **kwargs):
form = VideoGameForm(request.POST)
if form.is_valid():
obj = form.save(commit=False)
obj.updated_by = request.user
obj.save()
selected_categories = form.cleaned_data.get('category') #returns list of all selected categories e.g. ['Sports','Adventure']
#Now saving the ManyToManyField, can only work after saving the form
for title in selected_categories:
category_obj = Category.objects.get(title=title) #get object by title i.e I declared unique for title under Category model
obj.category.add(category_obj) #now add each category object to the saved form object
return redirect('confirmation', id=obj.pk)
Full Answer
models.py
class Category(models.Model):
title = models.CharField(max_length=100, null=True, unique=True)
class VideoGame(models.Model):
game_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=50, blank=False, null=False)
updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=False, on_delete=models.CASCADE)
category = models.ManyToManyField(Category) #ManyToMany Category field
date_added = models.DateTimeField(auto_now_add=True, verbose_name="date added")
forms.py ModelForm
class VideoGameForm(forms.ModelForm):
CATEGORIES = (
('Detective', 'Detective'),
('Sports', 'Sports'),
('Action', 'Action'),
('Adventure', 'Adventure'),
)
category = forms.MultipleChoiceField(choices=CATEGORIES, widget=forms.SelectMultiple())
class Meta:
model = VideoGame
fields = ['name', 'category', 'date_added']
views.py on POST
class HostingRequestView(View):
def post(self, request, *args, **kwargs):
form = VideoGameForm(request.POST)
if form.is_valid():
obj = form.save(commit=False)
obj.updated_by = request.user
obj.save()
selected_categories = form.cleaned_data.get('category') #returns list of all selected categories e.g. ['Sports','Adventure']
#Now saving the ManyToManyField, can only work after saving the form
for title in selected_categories:
category_obj = Category.objects.get(title=title) #get object by title i.e I declared unique for title under Category model
obj.category.add(category_obj) #now add each category object to the saved form object
return redirect('confirmation', id=obj.pk)
URL path for redirect
urlpatterns = [
path('confirmation/<int:id>/', Confirmation.as_view(), name='confirmation'),
]
I hope this can be helpful. Regards
new.stores.all()
returns all stores linked to the object.
Maybe:
Change Foo to Tracks
Tracks.objects.filter(id=x) to Tracks.objects.get(id=x)
Let me know how it goes
why this confusion so much.. you are getting the id there then, call the store like
new_track.save()
new_track.store.add(request.POST['store'])
I have a webpage where you can add a new computer/ip to the database, or choose a previously added from a dropdown list. Everything works fine, except that what I get after the user is selecting an item from the list is the index of it, and not its name.
How can I get the name? I am trying to access the queryset as a vector as seen here, but I get a TypeError.
views.py:
d = DropDownList(request.POST or None)
if d.is_valid():
c = request.POST.get('current')
return HttpResponse(d.qs[c-1])
models.py:
class DropDownList(forms.Form):
qs = Computer.objects.all().order_by('name')
current = forms.ModelChoiceField(qs, widget=forms.Select(attrs={"onChange":'submit()'}))
class Computer(models.Model):
name = models.CharField(max_length=200, default='')
ip = models.CharField(max_length=200, default='')
def __unicode__(self):
return self.name
home.html:
<form method="post">
{{ current }}
</form>
The whole point of using a form is that it takes care of validation and data conversion, so in your view you should get the data from the form, not from the POST itself.
if d.is_valid():
c = d.cleaned_data['current']