How to change an M2M field in a UpdateView? - python

SOLVED by Peter DeGlopper:
Thank you for your help it solved my issues, I really do appreciate it. I was banging my head against the table.
I didn't have to change my ModelForm. Looking at the HTML source I noticed in the input tag checked="checked" A subnet was being outputted as checked but it wasn't showing checked in my browser. This was in Firefox 24.2.0 in CentOS (On a VM), so I went to my Windows 7 host and loaded up Firefox 26.0 it worked, and worked fine in IE8 as well. That was weird, but it explains your confusion of that it should just work.
For saving the fields thanks to you I now see how I was over thinking it. And I am able to update the M2M field. I updated the TagUpdateView below to show the working code.
I have 2 issues with trying use an UpdateView with a M2M field...
The currently "tagged" subnets dont show up as checked in my template
How would I handle updating the M2M relationship in my TagUpdateView by overriding form_valid?
Any insight would be greatly appreciated.
Thanks.
Tag m2m models.py:
class Tag(models.Model):
tag = models.CharField(max_length=120)
group = models.ForeignKey(Group)
description = models.TextField(max_length=500)
subnet = models.ManyToManyField(Subnet, null=True, blank=True)
date_created = models.DateTimeField()
created_by = models.ForeignKey(User, related_name='tag_created_by')
date_modified = models.DateTimeField(auto_now=True)
modified_by = models.ForeignKey(User, related_name='tag_modified_by')
def __unicode__(self):
return self.tag
Tag ModelForm:
class TagForm(forms.ModelForm):
subnet = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple(), required=True, queryset=Subnet.objects.all())
class Meta:
model = Tag
exclude = ('date_created', 'created_by', 'date_modified', 'modified_by')
Tag views.py:
class TagUpdateView(UpdateView):
template_name = 'tag_update.html'
model = Tag
form_class = TagForm
def form_valid(self, form):
update_tag = form.save(commit=False)
update_tag.modified_by = self.request.user
update_tag.save()
form.save_m2m()
return super(TagUpdateView, self).form_valid(form)
My template "tag_update.html":
{% extends 'base.html' %}
{% load widget_tweaks %}
{% block title %}Tag {{ object.tag }} Update{% endblock %}
{% block content %}
<h1>Tag {{ object.tag }} Update</h1>
<br />
<form action="" method="post" role="form">{% csrf_token %}
<div class="row">
<div class="col-sm-4">
<div class="form-group">
<label for="id_tag">Tag Name</label>
{% render_field form.tag placeholder=form.tag.label class="form-control" %}
</div>
</div>
</div>
<div class="row">
<div class="col-sm-2">
<div class="form-group">
<label for="id_group">Group</label>
{% render_field form.group placeholder=form.group.label class="form-control"%}
</div>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label for="id_description">Description</label>
{% render_field form.description placeholder=form.description.label class="form-control" rows="5" %}
</div>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label for="id_checkbox">Link to Subnets</label>
{{ form.subnet }}
</div>
</div>
</div>
<br />
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<br />
{% endblock %}

You're overthinking it. Handling this kind of relationship can be a little bit complicated if you need to track information on the relationship model itself (like a modified timestamp for when a particular subnet/tag pair was created) but for the model relationships you've shown here, form.save_m2m() is sufficient - it handles the m2m relationship for you.
You wouldn't even need that if you didn't need to use commit=False on your initial form save so you can set your modified_by field.
For prepopulation - mostly this looks to me like it should follow the normal behavior and prepopulate. I would probably just use the widget class rather than explicitly instantiating it (widget=forms.CheckboxSelectMultiple rather than widget=forms.CheckboxSelectMultiple()) but I don't see why that would affect it.
For both problems, you might have good results by starting with a simple ModelForm with no customizations on subnet, just exclude set. Once that's working, put in the special view code to handle modified_by. Once that's working, change to a custom widget declaration for subnet - maybe initially using the meta widgets override dictionary rather than a custom field declaration for the first pass.

Related

why are comment objects not deleted from database although the view is triggered?

I try to remove comment so, I tried the first time by the class-based view then I hashed it to try the second way by normal function to know what's going on here. so, when I try to delete the comment by ID nothing does happen it's just directing me to the web page without deleting the comment so in this case the program runs but doesn't remove the object so, what is going on here?
Note: the posts and comments on the same page and slug field on that page are following by post not comment.
if the title of the post is: new title so, the slug will be new-title depending on the post
question_view.html
<div class="user-answer">
<div class="row">
<div class="col-xs-12">
{% for comment in my_question.comment_set.all %}
<div class="comments">
<div class="col-xs-0">
<div class="avatar">
<a href="{% url 'account:view_profile' comment.username %}">
<img class="img-circle img-thumbnail" style="width:50px; height: 50px;" src="{{ comment.logo }}">
</a>
</div>
</div>
<div class="col-xs-10">
<!-- --Comment itself-- -->
<div class="user_comment">
<p>{{ comment }}</p>
<div class="fa fa-caret-left comment-arrow"></div>
</div>
<!-- start Options in comment -->
<div class="sub-options">
{% if request.user.username == comment.username %}
<!-- --Edit comment-- -->
<div class="edit-comment">
<a>Edit</a>
</div>
<!-- --Delete comment-- -->
<div class="delete-comment">
<form method="post" action="{% url 'community:delete_comment' comment.pk %}">
{% csrf_token %}
<input type="hidden" name="delete-comment" value="{{ comment.comment }}">
<input type="submit" value="delete">
</form>
</div>
{% endif %}
<!-- end Options in comment -->
<!-- --comment Date-- -->
<div style="display: inline-block;color: #8e8e8e" class="comment-date">
<p>{{ comment.date|time }}</p>
</div>
</div>
</div>
<div class="clearfix"></div>
</div>
{% endfor %}
</div>
</div>
</div>
views.py
# Delete post
# class DeleteComment(DeleteView, SingleObjectMixin):
# model = Comment
# pk_url_kwarg = 'pk'
# template_name = 'community/question_view.html'
# queryset = Comment.objects.all()
#
# def get_success_url(self):
# title = UserAsking.objects.get(title=self.object)
# slug = UserAsking.objects.get(title=title).ask_slug
# return reverse_lazy('community:question_view', kwargs={'user_slug': slug})
#
# def get_object(self, queryset=None):
# request_comment = self.request.POST['delete-comment']
# return self.get_queryset().filter(pk=request_comment).get()
def delete_comment(request, pk):
if request.method == 'POST':
comment = Comment.objects.get(pk=pk)
del comment
return redirect('community:user_questions')
urls.py
urlpatterns = [
# path('delete-comment/<int:pk>/', views.DeleteComment.as_view(), name="delete_comment"),
path('delete-comment/<int:pk>/', views.delete_comment, name="delete_comment"),
]
models.py
class Comment(models.Model):
userasking = models.ForeignKey(UserAsking, on_delete=models.CASCADE)
comment = models.TextField(max_length=500, blank=True)
date = models.DateTimeField(auto_now_add=True)
userprofile = models.ForeignKey(UserProfile, on_delete=models.CASCADE, default=1)
name = models.CharField(max_length=30, blank=False, default='empty')
logo = models.ImageField(upload_to='images/', default='images/default-logo.jpg', blank=True)
username = models.CharField(max_length=30, blank=False, default='empty')
def __str__(self):
return self.comment
I hope if you can explain by class based-view what's happening I will appreciate that.
note that: if you could understand exactly what's happening you will know that is no error appears to me, therefore, I get no exception or traceback. thank you in advance.
Try this:
def delete_comment(request, pk):
if request.method == 'POST':
comment = Comment.objects.get(pk=pk).delete()
return redirect('community:user_questions')
Your function only deletes the object but not the database entry since you don't trigger a valid SQL operation (delete in this case).
You just delete the object comment again which was assigned previously, but you don't affect the database entry:
def delete_comment(request, pk):
if request.method == 'POST':
comment = Comment.objects.get(pk=pk)
del comment
return redirect('community:user_questions')
More on delete() in the official docu:
Deleting objects¶
Update on your comment:
You can't use Python's del statement to interact with database entries in Django.
Pythons del statement is used to delete objects initially created by Python like lists, variables, etc. etc.
In order to interact with your database in Django, you have to use the toolbox of Django's built-in Model instance which basically translates raw SQL operations into easy to use methods.
So maybe you can adapt a proper wording to highlight the differences by calling database "objects" entries and python objects: well, objects..
However, Django still offers the option to use raw SQL as fallback.

How to display django model data on the django cms page template

I would like to be able to use my external app data on django cms page.
I am able to use custom plugin data but not data from normal django app
I tried creating views to handle my data but how do I call this view from django cms pages?
here is exactly what I am asking for but his explanation is shallow and the link provided in the answer is no longer in use.
Here is my model:
class ExternalArticle(models.Model):
url = models.URLField()
source = models.CharField(
max_length=100,
help_text="Please supply the source of the article",
verbose_name="source of the article",
)
title = models.CharField(
max_length=250,
help_text="Please supply the title of the article",
verbose_name="title of the article",
)
class Meta:
ordering = ["-original_publication_date"]
def __str__(self):
return u"%s:%s" % (self.source[0:60], self.title[0:60])
My template has placeholders
{% load cms_tags %}
{% block title %}{% page_attribute "page_title" %}{% endblock title %}
{% block content %}
<section class="section">
<div class="container">
<div class="row">
<!-- header-->
<div class="col-lg-12">
<div class="updates">
{% placeholder "header" %}
</div>
</div>
<!-- header end-->
</div> <!-- end row -->
but I don't mind displaying this data anywhere on the template if not possible inside a place holder
I have a custom page that I am using in Django cms.
I would like to display the above data is a section in the Django cms page
If this model was inheriting from CMSPlugin then that would be easy because I could use a custom plugin in my placeholder
I expect to display the data from my model in the template.
I was able to achieve this by doing the following:
#plugin_pool.register_plugin
class ArticlesPluginPublisher(CMSPluginBase):
model = ArticlesPluginModel
name = _("Articles")
render_template = "article_plugin/articles.html"
cache = False
def render(self, context, instance, placeholder):
context = super(ArticlesPluginPublisher, self).render(
context, instance, placeholder
)
context.update(
{
"articles": Article.objects.order_by(
"-original_publication_date"
)
}
)
return context
The plugin model(ArticlesPluginModel) is just for storing the configurations for the instance of the plugin. Not the actual articles.
Then the render will just add to the context the relevant articles from the external app(Article)
You must somehow connect the ExternalArticle with a page object. For example
by defining the ExternalArticle as a page extension
or with an AppHook
or - low-tech - with a PageField on the ExternalArticle model
{% load cms_tags %}
<h1>{{ instance.poll.question }}</h1>
<form action="{% url polls.views.vote poll.id %}" method="post"> {% csrf_token %}
{% for choice in instance.poll.choice_set.all %}
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
<label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br />
{% endfor %}
<input type="submit" value="Vote" />
</form>

How to apply style to a Django form?

It is possible to change the style of form in the view?
I found in documentation this code:
<form action="/contact/" method="post">
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.subject.errors }}
<label for="id_subject">Email subject:</label>
{{ form.subject }}
</div>
<div class="fieldWrapper">
{{ form.message.errors }}
<label for="id_message">Your message:</label>
{{ form.message }}
</div>
<div class="fieldWrapper">
{{ form.sender.errors }}
<label for="id_sender">Your email address:</label>
{{ form.sender }}
</div>
<div class="fieldWrapper">
{{ form.cc_myself.errors }}
<label for="id_cc_myself">CC yourself?</label>
{{ form.cc_myself }}
</div>
<p><input type="submit" value="Send message" /></p>
</form>
But it just put a ugly input. I want to apply a class, or a css in this input. Is it possible?
This is my form:
class LoginView(NextUrlMixin, RequestFormAttachMixin, FormView):
form_class = LoginForm
success_url = '/'
template_name = 'accounts/login.html'
default_next = '/'
class LoginForm(forms.Form):
email = forms.EmailField(label='Email')
password = forms.CharField(widget=forms.PasswordInput)
If you are use a custom css set the class CSS on the forms example:
email = forms.EmailField(label='Email')
email.widget.attrs.update({'class':'customClass', 'required':'required'})
in this case set a customClass if you are using Bootstrap maybe you can use someone like this:
email = forms.EmailField(label='Email')
email.widget.attrs.update({'class':'form-control', 'required':'required'})
this code is on your forms.pyp .. good luck
don't forget load your css file on your template
You can do so by applying some attributes to the widget that you use:
more info can be found here Official Django Documentation
for instance:
email = forms.EmailField(label='Email',
widget=forms.TextInput(attrs={'class':'class_value','placeholder':"Email here"}))
If you want to have the full control of the form html by keeping the same behavior as the one generated by django, the following will work
<input type="email" name="email" id="id_email" value="{{form.email.value}}" class='class_name' attrs='attrs' >
{{ form.email.errors}} <!-- track errors for this field -->
Yes. You can attach a css file to your form (place a {{ form.media }} in your template) with this:
class LoginForm(forms.Form):
email = forms.EmailField(label='Email')
password = forms.CharField(widget=forms.PasswordInput)
class Media:
css = {
'all': ('login-form-layout.css',)
}
js = (
'https://some-cdn.com/some-framework.js'
'login-form-script.js',
)
Inputs have an ID like id_fiedname so login-form-layout.css can be something like:
#id_email, #id_password {
width: 200px;
}
You can do a lot with CSS and with javascript there are endless possibilities.
You may want to check "Customizing widget instances" at the official Django documentation. For example, you can attach arbitrary attributes to the input tag using the attrs argument:
email = forms.EmailField(
label='Email',
widget=forms.TextInput(attrs{
'class': 'my-super-special-input',
'placeholder': "mailbox#example.com"
}),
)
There are other possibilities there, so check it out.
Use Django widget_tweaks. It’s real convenient. And you can easily change any attribute you want.
{% load widget_tweaks %}
{% render_field form.field class=“your-class” %}

Django ClearableInputField in form doesn't render in HTML [duplicate]

This question already has an answer here:
Customize the styles of Django ClearableFileInput widget
(1 answer)
Closed 6 years ago.
I'm working on a web app that has photo upload functionality. I created a ModelForm to gather minimal user info plus a photo, and when I render it in HTML as {{ form.as_p }}, the field that allows the user to upload an image shows up just fine. The problem is, the form doesn't look good.
I need to be able to manually render the form in order to make it look better. I have written the HTML for this, and everything looks right except for the ImageFileField. Only the label gets rendered, not the upload button, checkbox to clear the file, etc.
What do I need to do to get the ImageFileField from the ModelForm to render correctly in my custom HTML? I've looked at the Django docs up and down, looked here on SO and can't find anyone else who's had this issue. Many thanks in advance!
forms.py snippet
class PostForm(forms.ModelForm):
class Meta:
model = Items
fields = ('title', 'description', 'image_file')
new_item.html snippet
<form enctype="multipart/form-data" method="post" action="" class="post-form">
{% csrf_token %}
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.title.errors }}
<label for="{{ form.title.id_for_label }}">Title:</label><br>
{{ form.title }}
</div><br>
<div class="fieldWrapper">
{{ form.description.errors }}
<label for="{{ form.description.id_for_label }}">Description: </label><br>
{{ form.description }}
</div><br>
<div class="fieldWrapper">
{{ form.image_field.errors }}
<label for="{{ form.image_field.id_for_label }}">Image (optional):</label><br>
{{ form.image_field }}
</div>
<button type="submit" class="save btn btn-default">Save</button>
</form>
models.py snippet
class Items(models.Model):
title = models.CharField(max_length=1000, null=False)
description = models.TextField(max_length=1000, null=False)
image_file = models.ImageField(max_length=1000,
blank=True,
default='',
null=True,
upload_to='item_photos')
By default django ModelForm uses django.forms.ImageField and not ClearableInputField for django.db.ImageField as revealed at https://docs.djangoproject.com/en/1.9/topics/forms/modelforms/#field-types
And I do believe you actually meant ClearableFileInput
ClearableFileInput¶
class ClearableFileInput File upload input: ,
with an additional checkbox input to clear the field’s value, if the
field is not required and has initial data.
How you can make use of it is by changing the widget in the class meta
class PostForm(forms.ModelForm):
class Meta:
model = Items
fields = ('title', 'description', 'image_file')
widgets = {
'name': ClearableFileInput(),
}
I ended up using the Chrome tool to inspect the HTML source for the page that rendered correctly (but ugly), and used that as a guide to custom build the form in HTML to my liking. This is what I needed to add into my HTML form to get it right:
{% if item.image_file %}
Currently:
{{item.image_file.url}}
<input id="image_file-clear_id" name="image_file-clear" type="checkbox" /> <label for="image_file-clear_id">Clear</label><br />Change: <input id="id_image_file" name="image_file" type="file" /></p>
{% endif %}
{% if not item.image_file %}
<input id="id_image_file" name="image_file" type="file" /></p>
{% endif %}

How to have a multiple select field in django in the form of a drop down box

Any help is greatly appreciated, I am a newbie in django.
class studentRegister(forms.Form):
courseList = forms.ModelMultipleChoiceField(queryset=Courses.objects.all())
Thank you and appreciate your time, I just want to modify this type of form so I can multiple select two or more options at a time and that returns in a list maybe?
One idea is work with Bootstrap classes and Python.
forms.py
class yourForm(forms.Form):
options = forms.MultipleChoiceField(
choices=[(option, option) for option in
Options.objects.all()], widget=forms.CheckboxSelectMultiple(),
label="myLabel", required=True, error_messages={'required': 'myRequiredMessage'})
view.py
def anything(...):
(...)
form = yourForm( )
(...)
return render(request, "myPage.html", {'form': form})
myPage.html
(...)
{% csrf_token %}
{% for field in form %}
<div class="col-md-12 dropdown">
<button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">{{ field.label_tag }}
<span class="caret"></span>
</button>
<div class="dropdown-menu">
<div>{{ field }}</div>
</div>
</div>
{% endfor %}
(...)
I think you can use the SelectMultiple widget. Source
class studentRegister(forms.Form):
courseList = forms.ModelMultipleChoiceField(queryset=Courses.objects.all(), widget=forms.SelectMultiple)
If this does not fit your needs, you can try using this snippet.

Categories