django admin using formset - django

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

Related

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

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

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()
)

Django : how to make rest api comment with Modelviewset

I tried to use Django's Modelviewset to implement the comment function, but there was a problem.
model :
class Comment(models.Model):
created = models.DateTimeField(auto_now_add= True)
content = models.CharField(max_length = 255, null = True)
author = models.ForeignKey(User, on_delete= models.CASCADE, null = True)
writer = models.CharField(max_length = 255, null = True)
class Meta:
abstract = True
ordering = ['-id']
class TalkComment(Comment):
title = "talk"
post = models.ForeignKey(Talk, on_delete= models.CASCADE, null = True)
serializers :
class TalkCommentSerializer(serializers.ModelSerializer):
class Meta:
model = models.TalkComment
fields = '__all__'
view :
class TalkCommentViewset(viewsets.ModelViewSet):
queryset = models.TalkComment.objects.all()
serializer_class = serializers.TalkCommentSerializer
url :
router.register(r'talk/(?P<id>\d+)/comment', postview.TalkCommentViewset)
/api/talk/2/comment/
All I wanted was to get the comments from that one post, but all the comments are coming.
How can I get a specific comment from a specific post?
You're getting all of the comments cause you initialized queryset to return all of the commnents. For getting comments of one specific post you must change the queryset. To do that you must override get_queryset method (cause you need to get post_id argument from request):
class TalkCommentViewset(viewsets.ModelViewSet):
serializer_class = serializers.TalkCommentSerializer
def get_queryset(self):
post_id = self.kwargs["id"]
queryset = TalkComment.objects.filter(post__id=post_id)
return queryset

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)

Validation fails on a select box whose contents are added by an Ajax call

This question is related to this one
Remove all the elements in a foreign key select field
I had a foreign key field in my model which was getting pre-populated by its data and I wanted the select list to be empty. I did achieve that but the validation fails when I submit the form.
The error says "Select a valid choice option. 1 is not one of the available choices).
These are my models
class RecipeIngredient(models.Model):
recipe = models.ForeignKey(Recipe)
ingredient = models.ForeignKey(Ingredient)
serving_size = models.ForeignKey(ServingSize)
quantity = models.IntegerField()
order = models.IntegerField()
created = models.DateTimeField(auto_now_add = True)
updated = models.DateTimeField(auto_now = True)
class RecipeIngredientForm(forms.ModelForm):
serving_size = forms.ChoiceField(widget=forms.Select())
class Meta:
serving_size = forms.ChoiceField(widget=forms.Select())
model = RecipeIngredient
fields = ('ingredient', 'quantity', 'serving_size')
widgets = {
'ingredient': forms.TextInput(attrs={'class' : 'recipe_ingredient'}),
'quantity': forms.TextInput(),
'serving_size' : forms.Select(attrs={'class' : 'ddl'}),
}
I get an error on the third line
recipeIngredients = models.RecipeIngredientFormSet(request.POST)
print(recipeIngredients.errors)
objRecipeIngredients = recipeIngredients.save(commit=False)
I want the select box to be empty because it gets populated by an ajax call. Any ideas what to do so the model passes the validation?
EDIT
Serving Size Model
class ServingSize(models.Model):
name = models.CharField(max_length = 255)
unit = models.CharField(max_length = 125)
food_group = models.ForeignKey(FoodGroup)
created = models.DateTimeField(auto_now_add = True)
updated = models.DateTimeField(auto_now = True)
objects = models.Manager()
dal_objects = ServingSizeManager()
def __unicode__(self):
return self.name;
First, why do you have serving_size in the Meta class?
I would use an extra field in the ModelForm and leave out serving_size field altogether.
class RecipeIngredientForm(forms.ModelForm):
mycustomfield = forms.ChoiceField(widget=forms.Select())
class Meta:
model = RecipeIngredient
exclude = ('serving_size', 'created', 'updated') #etc
Then in the view I would manipulate the form to assign a valid ServingSize to the serving_size field.
[EDIT]
Alright, your actual implementation will depend on what you are pulling through ajax and how. But see the following code: -
Your form: -
class CustomRecipeIngredientForm(forms.ModelForm):
recipe = forms.ModelChoiceField( Recipe.objects.all(),
widget=forms.Select(attrs={'class':'customclass',}))
ingredient = forms.ModelChoiceField( Ingredient.objects.all(),
widget=forms.Select(attrs={'class':'recipe_ingredient',}))
my_custom_serving_size_field = forms.ChoiceField(widget=forms.Select(attrs={'class':'ddl',}))
quantity = forms.IntegerField()
order = forms.IntegerField()
class Meta:
model = RecipeIngredient
exclude = ('serving_size', 'created', 'updated',)
Pull your data through ajax into the my_custom_serving_size_field
Your view: -
def my_view(request):
if request.method == 'POST':
form = CustomRecipeIngredientForm(data=request.POST)
if form.is_valid():
new_recipe_ingredient = form.save(commit=False)
new_recipe_ingredient.serving_size = ServingSize.objects.get(pk=form.cleaned_data['my_custom_serving_size_field'])
new_recipe_ingredient.save()
return HttpResponseRedirect(reverse('redirect_to_wherever'))
else:
form = CustomRecipeIngredientForm()
return render_to_response('path/to/my_template.html', {'form': form}, context_instance=RequestContext(request))
Of course, your ServingSize.objects.get() logic will depend on what your are pulling through ajax and how. Try something along these lines and let us know.
Looks like you want a ModelChoiceField, which
Allows the selection of a single model
object, suitable for representing a
foreign key