Django- how to render images in templates uploaded from admin? - django

I want to render images that I uploaded from my Django admin in my template views. Since I need to upload multiple images at a time, I declared a separate model, ShowPhoto, with a foreign key attached to my Problem model:
models.py
class Problem(models.Model):
slug = models.SlugField(null = False, unique = True, max_length = 255)
topic = models.ForeignKey(Topic, on_delete = models.CASCADE)
free = models.CharField(max_length = 1, choices = Free)
#problem when introducing UUID field
traceID = models.UUIDField(default=uuid.uuid4, editable = True)
#use meta tags to optimize SEO
metaTags = models.TextField(default = "")
questionToProblem = models.TextField()
class ShowPhoto(models.Model):
show = models.ForeignKey(Problem, on_delete = models.CASCADE, related_name = "photos")
photo = models.ImageField()
class Meta:
verbose_name = 'Solution Image'
verbose_name_plural = 'Solution Images'
Thus, in my admin.py, I also added:
class AdminImageWidget(AdminFileWidget):
def render(self, name, value, attrs=None, renderer = None):
output = []
if value and getattr(value, "url", None):
image_url = value.url
file_name = str(value)
output.append(u' <img src = "%s" alt="%s" width="600" height="600" style="object-fit: cover;"/> %s ' % \
(image_url, image_url, file_name, _('')))
output.append(super(AdminFileWidget, self).render(name, value, attrs))
return mark_safe(u''.join(output))
class ShowPhotoInline(admin.TabularInline):
model = ShowPhoto
formfield_overrides = {models.ImageField: {'widget': AdminImageWidget}}
#admin.register(Problem)
class ProblemModelAdmin(admin.ModelAdmin):
form = ProblemForm
list_display = ('questionToProblem', 'topic', 'free', 'traceID')
search_fields = ('questionToProblem', 'traceID')
readonly_fields = ('traceID',)
fields = ('slug', 'traceID', 'topic', 'free', 'metaTags', 'questionToProblem', 'solutionToProblem', 'photos') #'solution_on_webpage', 'photos')
inlines = [ShowPhotoInline]
def save_related(self, request, form, formsets, change):
super().save_related(request, form, formsets, change)
form.save_photos(form.instance)
How would I write a view to render the images that I upload in my admin? When I try to write a QuerySet using the filter command like this:
views.py
def displaySolution(request, topic_slug, problem_slug):
try:
solution = Problem.objects.get(topic__slug = topic_slug, slug = problem_slug)
image = ShowPhoto.objects.filter(show = solution)
except Exception as e:
raise e
return render(request, 'solution.html', {'solution' : solution, 'image' : image})
The QuerySet is called, but the rendering in the template is still blank. What do I need to do to fix it?

Have you tried to specify the local path you want your images to be uploaded, like this:
class ShowPhoto(models.Model):
show = models.ForeignKey(Problem, on_delete = models.CASCADE, related_name = "photos")
photo = models.ImageField(upload_to = 'static/img')
Don't forget to run python manage.py makemigrations and python manage.py migrate after changing models.py

Related

Django ManyToManyField Persistence Fails

I have a simple Django 3.1.0 app I need to create in order to assign Tasks with Tags (or assign tags into tasks).
My Model
class Task(models.Model):
user = models.CharField(max_length=33)
time = models.DateTimeField(auto_now_add=True)
task = models.CharField(max_length=500)
tags = models.ForeignKey('Tag', on_delete=models.SET_NULL, null=True)
class Tag(models.Model):
tag = models.CharField(max_length=30, default="No Tag")
members = models.ManyToManyField('Task')
class Meta:
verbose_name = "tag"
verbose_name_plural = "tags"
My Form
class TaskForm(ModelForm):
class Meta:
model = Task
fields = ['user', 'task', 'tags']
template_name = 'tasks.html'
tags = ModelMultipleChoiceField(
queryset=Tag.objects.values().all(), widget=CheckboxSelectMultiple()
)
My View
def main(request):
model = Task.objects.values().all()
form = TaskForm()
con = {'context': list(model), 'form': form}
if request.method == 'POST':
form = TaskForm(request.POST)
if form.is_valid():
obj = form.save(commit=False)
form.save_m2m()
return redirect('/')
else:
form = TaskForm()
return render(request, "tasks.html", con)
The migrations are successfull, and with the above code, the view shows a checkbox list with the fetched tags, but the problem is that when I hit Submit on the form, the values are not saved/written on the database but the page reloads successfully.
However, if I turn the following:
obj = form.save(commit=False)
form.save_m2m()
into
form.save(commit=True)
#form.save_m2m()
the values are written only from the fields 'user', 'task' - without the 'tags'
It's also funny that what fetches back on the webpage as values of the tags is in the shape of:
[checkbox] {'id': 1, 'tag': 'aks'}
What am I doing wrong? Thanks.
UPDATE after a comment below:
As Abdul Aziz suggested, I had to remove the values() from the queryset. But after that , to make it work, I had to add also:
In the model:
tag = models.CharField(max_length=100, default="No Tags")
and then refer to that one in the form and Vue template.
You have a ForeignKey set to the Tag model on your Task model, when you actually want a ManyToMany relationship between them. Remove the foreign key and set a related_name to the ManyToManyField in the Tag model like so:
class Task(models.Model):
user = models.CharField(max_length=33)
time = models.DateTimeField(auto_now_add=True)
task = models.CharField(max_length=500)
class Tag(models.Model):
tag = models.CharField(max_length=30, default="No Tag")
members = models.ManyToManyField('Task', related_name="tags")
class Meta:
verbose_name = "tag"
verbose_name_plural = "tags"
Also in your form you have:
tags = ModelMultipleChoiceField(
queryset=Tag.objects.values().all(), widget=CheckboxSelectMultiple()
)
Why are you using values here? Remove it:
tags = ModelMultipleChoiceField(
queryset=Tag.objects.all(), widget=CheckboxSelectMultiple()
)

How to filter data dynamically by supply values from form using django

I want to filter Blog Post objects or records based on the Post Category and a User that uploaded the Post record, it gives me an error when I try to do filter, this is the error.
ValueError at /dashboard/filter-post/
The QuerySet value for an exact lookup must be limited to one result using slicing.
Here is my models.py
class Category(models.Model):
cat_name = models.CharField(max_length=100, verbose_name='Category Name')
cat_desc = models.TextField(blank=True, null=True)
def __str__(self):
return self.cat_name
class Meta():
verbose_name_plural='Category'
class Post(models.Model):
pst_title = models.CharField(max_length=150)
pst_image = models.ImageField(blank=True, null=True, upload_to='uploads/')
user = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.ManyToManyField(Category)
content = models.TextField()
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.pst_title
#property
def img_url(self):
if self.pst_image:
return self.pst_image.url
on forms.py
class FilterForm(forms.ModelForm):
user = forms.ModelChoiceField(
queryset=User.objects.all(),
widget=forms.Select(attrs={'class': 'form-control'}))
category = forms.ModelMultipleChoiceField(
queryset=Category.objects.all(),
widget=forms.SelectMultiple(attrs={'class': 'form-control js-example-disabled-results'}))
catch_bot = forms.CharField(required=False,
widget=forms.HiddenInput, validators=[validators.MaxLengthValidator(0)])
class Meta():
fields = ['user', 'category' ]
model = Post
on views.py
def filter_post(request):
post = FilterForm(request.GET)
queryset = Post.objects.all()
if post.is_valid():
user=post.cleaned_data.get('user')
category=post.cleaned_data.get('category')
if user and category:
queryset = queryset.filter(user__username=user, category__cat_name=category)
return render(request, 'backend/filter-post.html', {'query':queryset, 'post':post})
I am having challenges properly filtering this in my views any help?
Try this:
instead of this:
queryset = queryset.filter(user__username=user, category__cat_name=category)
use this:
queryset = queryset.filter(user=user, category=category)
Also don't name your model fields after the model name, just use name instead of pst_name or cat_name, you will see that when you will try access these values there will be no confusion.
UPDATE
Ok, maybe try to rewrite your view like this:
def filter_post(request):
posts = Post.objects.all()
form = FilterForm(request.GET) # its best practice to call your form instance `form` in the view so that the next line has better readability
if form.is_valid():
user=post.cleaned_data['user']
category=post.cleaned_data['category']
if user:
posts = posts.filter(user=user)
if category:
posts = posts.filter(category=category)
return render(request, 'backend/filter-post.html', {'posts':posts})

Django - Why is my form invalid when a non-required field is not filled?

My form triggers form_invalid when the field "category" is empty.
The weird thing is, when the view displays, the "description" field does not have the asterisk indicating it's required, unlike "name" or "enabled", for instance. Also, when I try to send the form with an empty name, it correctly displays a little yellow mark and says "This field is required", but it doesn't say that when the category is empty.
So, it seems to correctly recognize that the category is not required, it just says it's invalid after I send the form.
My form looks like this:
class ProductForm(forms.Form):
name = forms.CharField(max_length=80, required=True)
category = forms.ModelChoiceField(queryset=None, required=False, label='Categoría')
description = forms.CharField(max_length=150, required=False)
price = forms.FloatField(required=True)
image = forms.ImageField(allow_empty_file=True, required=False)
extras = forms.FileField(allow_empty_file=True, required=False)
enabled = forms.BooleanField(required=False, initial=True)
def __init__(self, user, *args, **kwargs):
self.user = user
super(ProductForm, self).__init__(*args, **kwargs)
self.fields['name'].label = 'Nombre'
self.fields['description'].label = 'Descripción'
self.fields['price'].label = 'Precio'
self.fields['image'].label = 'Imagen'
self.fields['extras'].label = 'Extras'
categories = Category.objects.filter(store=Profile.objects.get(user=user).store)
if categories.count() == 0:
self.fields['category'].required = False
self.fields['category'].queryset = categories
self.fields['enabled'].label = 'Habilitado'
It is included to my view in this way:
class ProductCreateView(LoginRequiredMixin, CreateView):
template_name = 'products/product_form.html'
model = Product
fields = ["name", "category", "description", "price", "image", "enabled", "extra"]
success_url = reverse_lazy("orders:products")
And my model looks like this:
class Product(models.Model):
store = models.ForeignKey(Store, related_name="products", on_delete=models.PROTECT)
name = models.CharField(max_length=100, verbose_name="Nombre")
description = models.CharField(max_length=500, verbose_name="Descripción", null=True)
price = models.FloatField(verbose_name="Precio")
image = models.ImageField(upload_to="media/", verbose_name="Imagen", null=True, blank=True)
enabled = models.BooleanField(default=False, verbose_name="Habilitado")
extra = models.FileField(upload_to="media/files/", verbose_name="Extras", max_length=254, null=True, blank=True)
category = models.ForeignKey(Category, on_delete=models.PROTECT, null=True)
detail_enabled = models.BooleanField(default=False)
You never use the ModelForm you constructed. Django will create its own since nowhere you specify form_class=… [Django-doc] in your CreateView. But that will not be sufficient, since Django will not pass a user by default. You will need to override the .get_form_kwargs(…) [Django-doc] as well to pass the user.
You also should make the ProductForm a ModelForm, since otherwise it has no .save() method:
class ProductForm(forms.ModelForm):
# …
class Meta:
model = Product
fields = ['name', 'category', 'description', 'price', 'image', 'enabled', 'extra']
In your view you thus specify the form_class, and override the get_form_kwargs, to inject the user in the ModelForm constructor:
class ProductCreateView(LoginRequiredMixin, CreateView):
template_name = 'products/product_form.html'
model = Product
form_class = ProductForm
success_url = reverse_lazy('orders:products')
def get_form_kwargs(self, *args, **kwargs):
fk = super().get_form_kwargs(*args, **kwargs)
fk['user'] = self.request.user
return fk

django admin using formset

Models.py:
class ExperienceSynopsis(Audit):
user = models.ForeignKey(User, null = True, blank = True)
area_skill = models.CharField(max_length = 50, help_text = 'Example: Testing,Development,etc..')
experience = models.CharField(max_length = 50)
def __unicode__(self):
return self.area_skill
Forms.py:
class ExperienceSynopsisForm(forms.Form):
area_skill = fields.CharField(max_length=50, help_text = 'Example: Testing,Development,etc..')
experience = fields.CharField(max_length=50)
ExperienceFormset = formsets.formset_factory(ExperienceSynopsisForm, formset = RequiredFormSet, extra = 0)
I want to add formset in admin.py. How to enable admin for this model.Please help me.
The Django docs and tutorial give a headstart how this works
https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.StackedInline
Add your Formset to your Inline object and you're set up
class BookInline(admin.TabularInline):
model = Book
formset = # Yours
class AuthorAdmin(admin.ModelAdmin):
inlines = [
BookInline,
]
Use save_formset to override saving if needed

Auto populate DateTimeField not working in django forms

I am getting below error when I use auto_now_add in my Model Form.
TypeError: __init__() got an unexpected keyword argument 'auto_now_add'
Here is my model field
modified = models.DateTimeField(blank = True)
Declaration in form. I have seen in one of the posts DateTimeField Not Working
to add initial = datetime.datetime.now for auto populating
import datetime
modified = forms.DateTimeField(initial = datetime.datetime.now) - When I use this no error is coming but datetime was not auto populating.
I have used the same in self.fields['modified'] - Still no use
Any of the above statements were not working. Some one help me on this.
I am pasting all my model class and Model Form here
Model Class
class Users(models.Model):
name = models.CharField(max_length = 100)
role = models.ForeignKey(RolesConfig, db_column = 'role')
level = models.ForeignKey(LevelConfig, db_column = 'level')
team_name = models.ForeignKey(TeamNamesConfig, db_column = 'team_name')
location = models.ForeignKey(LocationConfig, db_column = 'location')
modified = models.DateTimeField(blank = True)
class Meta:
db_table = u'users'
def __str__(self):
return "%s" % (self.ldap)
def __unicode__(self):
return u'%s' % (self.ldap)
I have modified the field in phpmyadmin
This is my ModelForm
class TargetForm(forms.ModelForm):
modified = forms DateTimeField(initial = datetime.datetime.now)
def __init__(self, *args, **kwargs):
super(MMPodTargetForm, self).__init__(*args, **kwargs)
self.fields['modified'] = forms.DateTimeField(initial = datetime.datetime.now)
class Meta:
model = models.Users
I need to get current date and time autopopulated in the form, when the form loads. Tell me whats wrong in my code.
I think the error is because you're adding the auto_now_add extra argument to your form instead of to your mode. Try changing your model to the following to see if that fixes the problem (untested):
class Users(models.Model):
name = models.CharField(max_length = 100)
role = models.ForeignKey(RolesConfig, db_column = 'role')
level = models.ForeignKey(LevelConfig, db_column = 'level')
team_name = models.ForeignKey(TeamNamesConfig, db_column = 'team_name')
location = models.ForeignKey(LocationConfig, db_column = 'location')
modified = models.DateTimeField(auto_now = True)
class Meta:
db_table = u'users'
def __str__(self):
return "%s" % (self.ldap)
def __unicode__(self):
return u'%s' % (self.ldap)