How to write URL and VIEWS - django

Below is my model
class Movie(models.Model):
name = models.CharField(max_length=55)
artists = models.ManyToManyField(Artist)
def __str__(self):
return self.name
Below is the View:
def moviesView(request):
movies = Movie.objects.all()
context = {
"movies": movies
}
return render(request, 'movies/movies.html', context=context)
def movieView(request, name):
print(name)
movie = Movie.object.get(name=name)
context = {
"movie": movie
}
return render(request, 'movies/movie.html', context=context)
Below are the URLS:
urlpatterns = [
path('movies', moviesView, name='movies'),
re_path('movies/(\d+)', movieView, name='movie'),
path('artists', artistView, name='artists')
]
Below is the template:
<h1>Movies</h1>
{% for movie in movies %}
{{ movie.name }}
<h3>{{ movie.name }}</h3>
{% for artist in movie.artists.all %}
<ul>
<li>{{ artist.name }}</li>
</ul>
{% endfor %}
{% endfor %}
If I click a movie avengers, it should carry to another page with movie details of avengers as mentioned in the model:
I need to frame the url as: http://127.0.0.1:8000/movies/avengers

You did not specify a parameter. But even if you did, it would not accept it, since \d+ only accepts a sequence of digits, not strings.
You can work with:
urlpatterns = [
path('movies/', moviesView, name='movies'),
path('movies/(?P<name>.*)/', movieView, name='movie'),
path('artists/', artistView, name='artists')
]
but it is likely easier to work with path instead:
urlpatterns = [
path('movies/', moviesView, name='movies'),
re_path('movies/<str:name>/', movieView, name='movie'),
path('artists/', artistView, name='artists')
]
It is also not a good idea to work with a name. A lot of characters will be percent encoded [wiki] resulting in ugly URLs. Django uses slugs to make visually pleasant URLs.

Related

How to iterate in subitems of a object in a django template

views.py:
from django.views import generic
from .models import Servico
class ServicoView(generic.DetailView):
model = Servico
context_object_name = 'servico'
template_name = 'servico.html'
models.py:
from djongo import models
class PublicoAlvo(models.Model):
def __str__(self):
return ''
alvo1 = models.CharField(max_length = 126)
alvo2 = models.CharField(max_length = 126, blank = True, default = '')
class Meta:
abstract = True
class Servico(models.Model):
t_id = models.CharField(primary_key = True, unique = True, max_length = 252)
alvos = models.EmbeddedField(
model_container = PublicoAlvo
)
urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('servicos/<slug:pk>/', views.ServicoView.as_view(), name = 'servico')
]
I think these are the relevant files in my Django app folder. Back to the question, how can I iterate over the values that are going to be stored in servico.alvos in my template? If I want to show t_id, I just use {{ servico.t_id }} and it works fine. I could write something like:
<HTML1>
{{ servico.alvos.alvo1 }}
<HTML2>
<HTML1>
{{ servico.alvos.alvo2 }}
<HTML2>
And that would show the values that I want, but would make things uglier, since I would have to write a lot of repeated standard HTML (that I indicated as and ) to format each value inside servico.alvos, and more limited (imagine if I decide to change the model and add more 6 values in the PublicoAlvo class). I tried the following:
{% for alvo in servico.alvos.all %}
<HTML1>
{{ alvo }}
<HTML2>
{% endfor %}
and
{% for alvo in servico.alvos.items %}
<HTML1>
{{ alvo }}
<HTML2>
{% endfor %}
But I get nothing printed. When I try:
{% for alvo in servico.alvos %}
<HTML1>
{{ alvo }}
<HTML2>
{% endfor %}
I get 'PublicoAlvo' object is not iterable
Is there a way to get what I want using a loop in my template or changing something in my models.py?
Try
{{ servico.t_id }}
{{ servico.alvos }}
Then in your models.py
class PublicoAlvo(models.Model):
def __str__(self):
# Option 1: List all <alvo> fields
return ", ".join(
[
self.alvo1,
self.alvo2,
]
)
# Option 2: If you don't like manually listing each <alvo> field
# return ", ".join(
# [
# getattr(self, f"alvo{index}") for index in range(1, 3)
# ]
# )
...
This might give something like
The value for alvo1, While for alvo2 is this one
Update
You could also try
{% for key, value in servico.alvos.items %}
{% if key|slice:":4" == "alvo" %}
{{ value }}<br>
{% endif %}
{% endfor %}
This might show something like
The value for alvo1
While for alvo2 is this one
Based on the first answer of Niel Godfrey Ponciano, I was able to solve the problem.
models.py:
from djongo import models
class PublicoAlvo(models.Model):
def __str__(self):
return ''
def list(self):
return [value for key, value in self.__dict__.items() if not key.startswith('_')]
alvo1 = models.CharField(max_length = 126)
alvo2 = models.CharField(max_length = 126, blank = True, default = '')
class Meta:
abstract = True
class Servico(models.Model):
t_id = models.CharField(primary_key = True, unique = True, max_length = 252)
alvos = models.EmbeddedField(
model_container = PublicoAlvo
)
And then I can iterate over servico.alvos.list using a for in the template just by adding the list method that returns the relevant fields (variables) values in my class.

Django filtering database items

I am trying to build a project where I have a DB with toys where there are brands, each brand have products, the products have variety of sizes and sizes have colors. What I want to achieve is to have a home page with filtered the brands which I manage to achieve with the views.py below:
def Index(request):
toys = Toys.objects.values('brand').distinct().order_by('brand')
context = {'index': toys }
return render(request, 'index.html', context)
with models.py
class Toys(models.Model):
brand= models.CharField(max_length=255)
product = models.CharField(max_length=255)
size = models.CharField(max_length=255)
color = models.CharField(max_length=255)
Now on the main page I have a list with all the different brans, what I'm trying to do is when I click on any Brand for the home page to redirect me to a result page with list of the Brand's products, product will lead to all available sizes and so on.
How to implement that in the views?
Many approaches are possible. If you do not wish to have better functionality (e.g. all toys from brand 'X' are deleted when the brand is deleted from database), you could do the following.
Write a view function for each "level" of your listing. In the context dictionary, index contains the query result, listing is a heading to be displayed and level is a variable to select how the link is constructed in the template.
from django.shortcuts import render
from toyapp.models import Toys
def Index(request):
toys = Toys.objects.values('brand').distinct().order_by('brand')
context = { 'index': toys,
'listing': 'Toy brands:',
'level': 'brands',
}
return render(request,'toyapp/index.html', context)
def brandview(request, slug):
toys = Toys.objects.filter(brand=slug).order_by('product')
context = {'level': 'products',
'index': toys,
'listing': 'Products from brand ' + slug,
}
return render(request,'toyapp/index.html', context)
def productview(request, slug1, slug2):
toys_set = Toys.objects.filter(brand=slug1)
toys = toys_set.filter(product=slug2).order_by('size')
context = {'level': 'sizes',
'index': toys,
'listing': 'Sizes for product ' + slug2 + ' from brand ' + slug1
}
return render(request,'toyapp/index.html', context)
def sizeview(request, slug1, slug2, slug3):
toys_set = Toys.objects.filter(brand=slug1)
toys = toys_set.filter(product=slug2).order_by('size')
context = {'level': 'colors',
'index': toys,
'listing': 'Colors for size ' + slug3 + ' products ' + slug2 + ' from brand ' + slug1
}
return render(request,'toyapp/index.html', context)
The template index.html is quite simple with a {% for %} loop to go through items and {% if %} tags to select what kind of a link to create and what text to display.
<!DOCTYPE html>
<body>
<h2>{{ listing }}</h2>
{% for item in index %}
<li>
{% if level == 'brands' %}
<a href="{% url 'brandview' slug=item.brand %}" >{{item.brand}}</a>
{% elif level == 'products' %}
<a href="{% url 'productview' slug1=item.brand slug2=item.product %}" >{{item.product}}</a>
{% elif level == 'sizes' %}
<a href="{% url 'sizeview' slug1=item.brand slug2=item.product slug3=item.size %}" >{{item.size}}</a>
{% elif level == 'colors' %}
{{item.color}}
{% endif %}
</li>
{% endfor %}
</body>
Finally, you need to write urls.py with urlpatterns that parses the HTTP request address and passes the arguments to the view.
from django.urls import path, include
from . import views
urlpatterns = [
path('', views.Index, name='index'),
path('<slug:slug>', views.brandview, name='brandview'),
path('<slug:slug1>,<slug:slug2>', views.productview, name='productview'),
path('<slug:slug1>,<slug:slug2>,<slug:slug3>', views.sizeview, name='sizeview'),
]

Filtering field in DJango in the URL and in the view

Lets say I have a model with 2 fields:
class Bands(models.Model):
id = models.IntegerField(db_column='ID', primary_key=True)
name = models.CharField(db_column='NAME')
type = models.CharField(db_column='TYPE')
...
What I want to do is to list all the fields data with just one template. For example:
{% block content %}
{{ field_name }}
<ul>
{% for band in bands %}
<li>{{ band }}</li>
{% endfor %}
</ul>
{% endblock %}
So, how should I make my url?
url(r'^band/$', views.band, name='band')
or
url(r'^band/(?P<band>\w+)/$', views.band, name='band')
The link to that page would be:
Name
In the view I'm taking the values as this:
def band(request, field):
results = Results.objects.all()
names = [n.name for n in results]
types = [t.type for t in results]
if field == 'name':
bands = names
else:
bands = types
return render(request, 'band.html', {'bands': bands, 'field_name': field})
Is this the right way to do this (in the view and the url)? Thanks in advance.
Well, the simplest thing to do is use the DetailView.
from .models import Band
class BandDetailView(DetailView):
model = Band
And in urls.py something like:
from band.views import BandDetailView
url(r'^band/(?P<pk>\d+)/?$', BandDetailView.as_view(), name='band-detail')
And in the template:
{% url 'band-detail' pk=1 %}
That said, your model doesn't make much sense to me, as does the Led Zeppelin vs. Deep Purple bits in the view. Can you explain your project / need a bit more?

Add custom html to choicefield label in django

I am struggling with a requirement now, I want to add an image to choice field label and I really dont have a clue how to do it. I am using django form wizard to render the form.
Here is the image which shows what I want to achieve :
And here is what I have got right now ( to make the radio buttons inline, I know it could be achieved through css):
Here is the forms.py:
from django import forms
from django.utils.translation import gettext as _
CHOICES=[('0','Pay by card'), ('1','Invoice')]
class PaymentForm(forms.Form):
title = 'payment'
payment_method = forms.ChoiceField(label = _("Payment Options"), choices=CHOICES, widget=forms.RadioSelect(), required = True)
I am rendering using wizard form:
{{ wizard.form.payment_method.label_tag }}
{{ wizard.form.payment_method|safe }}
{{ wizard.form.payment.errors}}
Anyone has any suggestion for this apart from custom widget?
Without a widget:
from django.utils.safestring import mark_safe
CHOICES=[('0', mark_safe('Pay by card <img src="by_card.jpg"/>')),
('1', mark_safe('Invoice <img src="no_card.jpg"/>'))
]
Credit: setting help_text for each choice in a RadioSelect
1) Shortly (but i'm not sure) call a function where 'Pay by card' and return all <img>... that you need.
2) You can make somthing like #Gahbu said
3)Long [Better, i think, but untested :( ]:
Make a renderer:
from myapp.my_widgets import CardsRadioFieldRenderer
CARD_CHOICE = '0'
CHOICES=[(CARD_CHOICE,'Pay by card'), ('1','Invoice')]
class PaymentForm(forms.Form):
title = 'payment'
payment_method = forms.ChoiceField(label = _("Payment Options"), choices=CHOICES,widget=forms.RadioSelect(renderer=CardsRadioFieldRenderer), required = True)
# myapp/my_widgets.py
class CardRadioInput(RadioInput):
def __init__(self, name, value, attrs, choice, index):
self.name, self.value = name, value
self.attrs = attrs
choice_value = force_text(choice[0])
self.choice_value = choice_value
if choice_value == CARD_CHOICE:
choice_label = force_text(self.get_html_for_card_choice())
else:
choice_label = force_text(choice[1])
self.choice_label = choice_label
self.index = index
def get_html_for_card_choice(self):
#some logic to get the images tags (<img ...> <img ...>)
return text
class CardsRadioFieldRenderer(RadioFieldRenderer):
def __getitem__(self, idx):
choice = self.choices[idx] # Let the IndexError propogate
return CardRadioInput(self.name, self.value, self.attrs.copy(), choice, idx)
In your template do something like :
{% for choice in wizard.form.payment_method.choices %}
{{ choice.0 }} {# value #} {{ choice.1 }} {# value #}
{% if choice.0 == PAYMENT_BY_PAYPAL %}
...
{% endif %}
{% endfor %}
You can also write :
{% for key, value in wizard.form.payment_method.choices %}
{% if key == PAYMENT_BY_PAYPAL %}
...
{% endif %}
{% endfor %}
If you want to tweak it from the template, you need to use the "safe" which tells Jinja not to escape it. like below
{{ some_object.some_property|safe }}
Yet if you want to use it inside your python code while defining the form/modelform just use mark_safe as below.
mark_up = 'this is a link'
choices = list()
choices.append(('some_id', mark_safe(mark_up)))
self.fields['some_field'].choices = choices

Django-Provide a download from a function defined in a model

I am trying to implement a view, where a logged in user that has uploaded a file can download his file, but only his, not other users files, so I don't create an id url based on the pk of the file . As a result in the views I query the test_result_file table and I filter it for a specific user. I think that I can do what I want by writing a function in my model:
class test_result_file(models.Model):
user=models.ForeignKey(User)
system=models.ForeignKey(system)
test_id=models.ForeignKey(Detail)
path=models.CharField(max_length=300)
class Meta:
verbose_name="Test Result file"
verbose_name_plural="Test Result files"
def get_self(self):
path=self.path
wrapper = FileWrapper(open( path, "r" ))
response=HttpResponse(wrapper, content_type="text/plain")
response['Content-Disposition'] ='attachment; filename="results.txt"'
return response
However, in the template, when I call:
<ul>
{% for at in attempts %}
<li>System Name: <em>"{{ at.system}}"</em>, download file: here </li>
{% endfor %}</ul>
the download is not provided and instead the browser tries to open a url with all the parameters of response and fails.
Am I losing something? Is this feasible with that function?
First point: a response is not an url. What you want in your template are urls, not responses. Second point: generating a reponse is the responsability of a view, not of a model. Side note : you should respect Python's coding conventions (cf pep08)
The RightWay(tm) to organize your code would be:
# myapp/models.py
class TestResultFile(models.Model):
user=models.ForeignKey(User)
system=models.ForeignKey(System)
test_id=models.ForeignKey(Detail)
path=models.CharField(max_length=300)
class Meta:
verbose_name="Test result file"
verbose_name_plural="Test result files"
# myapp/views.py
def download_file(request, file_id):
testfile = get_object_or_404(TestResultFile, pk=file_id)
wrapper = FileWrapper(open(testfile.path, "r" ))
response=HttpResponse(wrapper, content_type="text/plain")
response['Content-Disposition'] ='attachment; filename="results.txt"'
return response
# myapp/urls.py
urlpatterns = patterns('',
url(r'^download/(?P<file_id>\d+)/?', 'views.download_file', 'myapp_download_file'),
# ...
)
# myapp/templates/myapp/template.html
<ul>
{% for at in attempts %}
<li>
System Name: <em>"{{ at.system}}"</em>,
download file: here
</li>
{% endfor %}
</ul>
Maybe you can simply use somthing like this:
class userfile(model.Model):
user=models.ForeignKey(User)
file = models.FileField(_('file'), upload_to='userfile/', blank=False)
def __unicode__(self):
return "%s file" % user
and in you template:
{% if user.userfile_set.count > 0 %}
<ul>
{% for file in user.userfile_set.all %}
<li>File: {{file}} dowload it</li>
{% endfor %}
</ul>
{% else %}
You don't have any file
{% endif %}
I hop it can help you.