User entered links display as text in Django - django

I just finished creating a user commenting system on a social networking app I am building with Django (python version 2.7.8, Django verion 1.6).
Everything is working well with the commenting system, but I have encountered an issue. If a user submits a link to an external site in one of their comments, that link appears as plain text. I would like the user submitted link to automatically be viewed as a link to that other users can click on.
Does anyone know a potential solution to this problem?
models.py
class Comment(models.Model):
#Model that defines the Commenting system
created = models.DateTimeField(editable =False)
author = models.CharField(max_length = 200, editable = False)
body = models.TextField()
item = models.ForeignKey(BucketListItem)
def __unicode__(self):
return self.body
comment-template.html
<h2>Comments:</h2>
<br>
{% if comments %}
{% for comment in comments %}
<div class = "comment-div">
<h5>{% avatar comment.author 40 %}</h5>
<h5> {{comment.author}}</h5>
<h5 class ="timesince">{{ comment.created|timesince}} ago.</h3>
<br>
<br>
<p>{{comment.body}}</p>
{% if comment.author == current_user %}
<span class = "fa fa-close"></span>
{% endif %}
</div>
{% endfor %}
<br>
<hr>
<br>
{% else %}
<p>There are no comments yet. Be the first to add one!</p>
{% endif %}
<h5 class = "leave-comment">Leave a Comment Here: </h5>
<br>
<form action="/bucketlist/item/{{id}}/" method = "post" role = "form">
<div class = "form-group">
{% csrf_token %}
{% for field in form %}
{{ field.errors }}
{{ field }}
<br>
{% endfor %}
<br>
<input type = "submit" value = "Submit" class="btn btn-warning">
</div>
<br>

You should be able to do this using the urlize template tag that Django provides.
<p>{{ comment.body | urlize }}</p>
This should convert any links within the body of the comment to an actual <a> tag.

Related

How to access ManyToMany field without loop inside template

I have the following models defined for my portfolio projects:
class Technology(models.Model):
title = models.CharField(max_length=10)
def __str__(self):
return self.title
class Meta:
verbose_name_plural = 'Technologies'
class Project(models.Model):
title = models.CharField(max_length=100)
description = HTMLField()
technology = models.ManyToManyField(Technology)
image = models.ImageField(upload_to='projects/')
def __str__(self):
return self.title
And I have this in my views.py:
def index(request):
projects = Project.objects.all()
context = {'projects': projects}
return render(request, 'homepage/index.html', context)
And the following piece of HTML to display the projects:
{% for project in projects %}
<div class="col-lg-4 col-md-6 portfolio-item filter-django">
<div class="portfolio-wrap">
<img src="{% static 'homepage/img/portfolio/portfolio-1.jpg' %}" class="img-fluid">
<div class="portfolio-info">
<h4>{{ project.title }}</h4>
<p>FIRST TECHNOLOGY GOES HERE</p>
</div>
</div>
</div>
{% endfor %}
My challenge is the <p>...</p> tag because each project has multiple technologies, but I need to print the first one only so I can't have a for loop here.
I have tried {{ project.technology.[0] }} but that gives me Could not parse the remainder: '[0]' from 'project.technology.[0]'
Please assist.
Django template language (DTL) does not support the various complex syntax that python does. This is done intentionally because one of the aims of DTL is to separate business logic from presentation logic. Hence {{ project.technology.[0] }} does not work. But instead all sorts of lookups can be performed using the . operator itself, meaning you can write {{ project.technology.all.0 }} (Here since all is callable it will get called by the template engine) to achieve the same effect as indexing.
But instead you should use the first [Django docs] method of the queryset instead which will give you either the first object or None (The [0] indexing can raise an exception if the resulting query does not return anything):
{% for project in projects %}
<div class="col-lg-4 col-md-6 portfolio-item filter-django">
<div class="portfolio-wrap">
<img src="{% static 'homepage/img/portfolio/portfolio-1.jpg' %}" class="img-fluid">
<div class="portfolio-info">
<h4>{{ project.title }}</h4>
{% with first_technology=project.technology.first %}
{% if first_technology is not None %}
<p>{{ first_technology }}</p>
{% else %}
<p>No technology</p>
{% endif %}
{% endwith %}
</div>
</div>
</div>
{% endfor %}

Quick help on how to format a logic statement

this might seem silly, but i need help,
I have a model, in which if "is_vendor" is True, I want it to display something, while if "is_vendor" is False, I dont want the item to display. I already figured how to switch the is_vendor from True to False or vice versa, What i want now is to know how to complete {% if user_profile.is vendor... statement (Plus Im not sure if want i typed there is close to correct. Thank you
Model:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.CharField(max_length=245, null=True)
image = models.ImageField(default='default.png', upload_to='profile_pics')
is_vendor = models.BooleanField(default=True)
My template:
**{% if user_profile.is_vendor**
<div style="margin-left: 40px">
<a class="btn btn-sm btn-outline-primary mb-4 mr-3 "href="{% url 'vendor_register' %}">
Register
</a>
</div>
{% if user_profile.isVendor %}
<div style="margin-left: 40px">
<a class="btn btn-sm btn-outline-primary mb-4 mr-3 "href="{% url 'vendor_register' %}">
Register
</a>
</div>
{% endif %}
{% if user_profile.isVendor %}
your code in here
{%endif %}
If the user is logged in, you can access that user with {{ user }}. Since you have a OneToOneField to the Profile model, you thus can access the Profile record of the logged in user with {{ user.profile }}, and thus check if it is a vendor with {{ user.profile.is_vendor }}, we thus can wrap this in an {% if … %} … {% endif %} template tag [Django-doc]:
{% if user.profile.is_vendor %}
…
{% endif %}

Django BooleanField - Checkbox not showing in form on website

I have the following:
forms.py
class StoreSettingsForm(ModelForm):
enable_repricer = forms.BooleanField(required=True)
enable_ignore_min_price = forms.BooleanField(required=True)
class Meta:
model = Store
fields = '__all__'
exclude = ['user']
models.py
class Store(models.Model):
user = models.ForeignKey(User, models.SET_NULL, blank=True, null=True)
store_name = models.CharField(max_length=100, blank=False)
...
enable_repricer = models.BooleanField(default=False)
enable_ignore_min_price = models.BooleanField(default=False)
template.html
<form method="post">
{% csrf_token %}
<h4>Repricer Settings</h4>
<div class="row">
{{ form.non_field_errors }}
<div class="fieldWrapper col s4">
{{ form.enable_repricer.errors }}
<label for="{{ form.enable_repricer.id_for_label }}">Enable Repricer:</label>
{{ form.enable_repricer }}
</div>
<div class="fieldWrapper col s4">
{{ form.enable_ignore_min_price.errors }}
<label for="{{ form.enable_ignore_min_price.id_for_label }}">Allow Repricer to ignore min_price:</label>
{{ form.enable_ignore_min_price }}
</div>
<div class="fieldWrapper col s4">
{{ form.repricer_factor.errors }}
<label for="{{ form.repricer_factor.id_for_label }}">Repricer aggressiveness factor (1-100):</label>
{{ form.repricer_factor }}
</div>
</div>
<input class="btn" type="submit" value="Submit">
</form>
</div>
view.py
class StoreSettingsView(View):
template_name = 'store_settings.html'
def get(self, *args, **kwargs):
store = Store.objects.get(id=kwargs['id'])
data = {
'store_name': store.store_name,
'store_email': store.store_email,
...
'enable_ignore_min_price': store.enable_ignore_min_price,
'enable_repricer': store.enable_repricer,
'repricer_factor': store.repricer_factor,
}
form = StoreSettingsForm(initial=data)
return render(self.request, self.template_name, {
"store": store,
"form": form,
})
It does not show up in the form. All field are showing on the page but not the 2 boolean fields. The labels are showing and in the HTML.
I have excluded many fields from the code blocks to make it more easy to read.
I was with this problem and I solved it doing that:
#----- My error was caused by Materialize css ----#
Not showing checkbox
<form>
{% for field in form %}
{{field.label_tag}}<br>
{{field}}
<br>
<br>
{% endfor %}
</form>
Using Materialize, You must have a span tag in your label:
Example in Materialize doc:
<label>
<input type="checkbox" class="filled-in" checked="checked" />
<span>Filled in</span>
</label>
I solved the problem refacting to the Materialize's doc structure:
<form>
{% for field in form %}
<label>
{% if field.label == "colorida" %} <!-- 'colorida' is my boolean field -->
{{field}}
<span>{{field.label}}</span> <!-- if boolean field, span must be after input (in materialize) -->
{% else %}
<span>{{field.label}}</span> <!-- description before input if not boolean field -->
{{field}}
{% endif %}
</label>
<br>
<br>
{% endfor %}
</form>
Test your example on a page without any CSS, the problem probably is caused by CSS
Ok. When typing the question something arose to me. What if.... and yes that was it. In some way my CSS is disabling the checkbox and i really do not know why. When removing the CSS it works fine. Sorry.

Django many-to-many field in template and through

In my models there are some manytomany fields. I've been struggling to make them appear in the template. One is a regular ManyToMany field, the other one uses through. The problem is that the amount is not shown. I understand that currently the iteration is only defined for component in pcbuilds.components.all. How to manage the amount in there as well?
models.py:
class Component(models.Model):
name = models.CharField(max_length=100, unique=True,help_text='Component name')
manufacturer = models.IntegerField(blank=True,null=True,choices=MANUFACTURERS)
model = models.CharField(max_length=100,unique=True,blank=True,null=True, help_text='model')
class Tag(models.Model):
title = models.CharField(max_length=100,unique=True,blank=True,null=True, help_text='tagname')
class PCBuilds(models.Model):
title = models.CharField(max_length=50, help_text='PC build title')
components = models.ManyToManyField(Component,help_text='Pick your components from the list or create and add them.',through='Componentlist')
subtitle = models.CharField(blank=True,max_length=80, help_text='Subtitle')
def __str__(self):
return self.title
class Componentlist(models.Model):
component = models.ForeignKey(Component, on_delete=models.CASCADE,related_name='components')
pcbuild = models.ForeignKey(PCBuilds, on_delete=models.CASCADE,related_name='pcbuilds')
amount = models.FloatField(null=True,help_text='amount')
template:
<div class="card-deck">
{% for pcbuilds in object_list|slice:":3" %}
<div class="card" style="width: 18rem;">
<div class="card-header">
<a href="{% url 'pcbuilds_detail' pcbuilds.pk %}">
<span class="font-weight-bold">{{ pcbuilds.title }}</span></a> ·
<span class="text-muted">{{ pcbuilds.subtitle }}</span>
</div>
<div class="card-body">
<ul>
{% for component in pcbuilds.components.all %}
<li>{{ component.name}}{{ component.manufacturer}}{{ component.model }}{{ componentlist.amount }}</li>
{% endfor %}
</ul>
</div>
<div class="card-footer text-center text-muted">
{% for tag in recipe.tags.all %}
Tags: {{ tag.title }}
{% endfor %}
</div>
</div>
<br />
{% endfor %}
You don't define componentlist there, so the template will ignore it. Normally, you need to follow a relationship, as you did to get component in the first place. But this way there is no access to the through table, as you've already effectively gone past it to get the target table.
Instead you need to follow the relationship from pcbuild to the through table, and from there to the component:
{% for componentlist in pcbuilds.pcbuilds.all %}
<li>{{ componentlist.component.name}}{{ componentlist.component.manufacturer}}{{ componentlist.component.model }}{{ componentlist.amount }}</li>
{% endfor %}
Note, your related names in that through table are strange, which is why I had to use that confusing pcbuilds.pcbuilds.all. The reverse relationship from pcbuild to componentlist should be componentlist_set, which is the default; there shouldn't be any reason to change that.

Why aren't labels appearing in my Django ModelForm?

I have a very simple ModelForm in my app that looks like this:
# ModelForm
class ProductForm(ModelForm):
class Meta:
model = MyModel
exclude = ['created', 'last_modified', 'serial_number']
# Model
class BaseModel(models.Model):
created = models.DateTimeField(auto_now_add=True, blank=True, null=True)
last_modified = models.DateTimeField(auto_now=True, blank=True, null=True)
class MyModel(BaseModel):
product = models.TextField(verbose_name='Product Name')
serial_number = models.TextField(verbose_name='Serial Number')
And a form that looks like this:
# Form
<form method="POST" action="{% url some_url %}">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
{% for field in form %}
{% if field.errors %}
<div>{{ field.errors }}</div>
{% endif %}
<div>
{{ field.label_tag }}:
{{ field }}
</div>
{% endfor %}
{% endfor %}
<div class="actions">
<input class="button submit focus" type="submit" value="{% trans "Save" %}" />
</div>
</form>
When I check out the view using this, I just see a colon (:) followed by the textfield: The label is gone.
According to the documentation for ModelForm:
In addition, each generated form field has attributes set as follows:
...
The form field’s label is set to the verbose_name of the model field, with the first character capitalized.
What mistake have I made?
I am using Django 1.4.1 if it matters.
You have to put the field label inside a <label> tag. So:
<div>
<label for="id_{{field.html_name}}">{{field.label}}:</label>
{{ field }}
</div>
The only solution I managed to find that still allowed me to separate each row of the form was doing the following:
<form method="POST" action="{% url some_url %}">
{% csrf_token %}
{{ formset.as_ul }}
<div class="actions">
<input class="button submit focus" type="submit" value="{% trans "Save" %}" />
</div>
</form>
... the key piece being the {{ formset.as_ul }} instead of iterating through each field.
As for why the other listed solution (or the solution in the documentation) didn't work, I will remain baffled.