I've looked around and haven't found a satisfactory solution. I am trying to uploaded an image via the Django admin interface using models and then call that uploaded image to be used as the background for a div .i.e - I create football called Salford City, I upload a picture of the team. I will have several teams. If I choose Salford then the background changes, if I choose Brighton Albion then the background changes. I have it working using javascript and hard coded cities but I would like to be able to do it dynamically without have to change base coding to enable easy scaling. Current code is as follows:
Models.py:
class City(models.Model):
index = models.CharField(unique=True, max_length=250, null=False, blank = False, verbose_name = 'City Inner Search Name(lowercase)')
landing_page_description = models.CharField(max_length=5000, null=True, blank = True, verbose_name = 'City Description')
name = models.CharField(max_length=250, null=False, blank = False, verbose_name = 'City Common Name')
country = models.ForeignKey(Country, null=True, blank=True)
image = models.ImageField(upload_to="images/", blank=True, null=True, verbose_name= 'City Image')
The images are stored in media_files/images
The html:
<div id="main" class="container-fluid citybackground">
<!-- <img src="{{ MEDIA_URL}}{{ City.image.url }}"> -->
AS you can see I tried multiple options. The commented out section was a method I tried.
The css:
.citybackground
{
background-image: url( "{% images {{City.image.url}} %}");
background-repeat:no-repeat;
background-size:100%;
}
The views.py:
def SetCitySession(request):
if request.method == "POST":
request.session['city-name'] = request.POST['cityName']
request.session['city-id'] = request.POST['cityId']
return JsonResponse({})
def GetCitySession(request):
if request.method == "GET":
cityName = request.session['city-name']
cityId = request.session['city-id']
context = {
"cityName": cityName,
"cityId": cityId
}
return JsonResponse(context)
This is currently working hard coded javascript which I want to stop using since it isn't dynamic.
function changeBackground(city){
var urlBackground;
switch(city){
case "Manchester":
urlBackground = "/static/img/fondo_main.jpg";
break;
case "Liverpool":
urlBackground = "/static/img/liverpool_background.jpg";
break;
case "London":
urlBackground = "/static/img/london_background.jpg";
break;
}
$("#main").css("background-image", "url('{% if city.image %}{{ city.image.url }}{% else %}{"+ urlBackground + "}{% endif %}')";
}
Thanks ya'll
Simply write it as:
background-image: url({{ city.image.url }});
Use inline style for background image
<div id="main" class="container-fluid citybackground" style="background-image: url({{City.image.url}})">
I wrote this question when I was completely new to Django, finishing off an existing project whilst not fully understanding how the system worked. The answer by #yusuf.oguntula is correct in a manner. In the the question I neglected to add the view that actually showed the background image and rather gave the views which where used to retrieve and set session data on the server. Secondly, the actual view did not state which city it was since it had not retrieved the city from django. At the time the city was set by javascript but there was no query set or object retrieval within the view itself so as to allow interaction with python syntax. This meant even though the solution suggested by #yusuf.oguntula was correct it did not work due to the missing query set. I fixed this first by retrieving the appropriate city to the HomepageView in views.py like this:
if 'city-name' not in request.session:
cityName='Madrid'
request.session['city-name'] = request.POST.get('cityName')
else:
cityName = request.session['city-name']
city = get_object_or_404(City, name=cityName)
and then used #yusuf.oguntula 's solution in my html. It worked perfectly.
Add inline CSS to the element you want to have a background image. For example,
<div style="background: url('{{ home.image.url }}');">
</div>
This will work for sure
Related
I am trying to display a User's name on top of a box where they enter their Employee # in a form, without having to refresh the page.
For example, they enter their # and then after they click/tab onto the next field, it renders their name on top, which comes from the database, so the user knows they've entered the correct info. This name is stored in a separate model, so I try to retrieve it using the "id/number".
I am not too familiar with AJAX but after reading a few similar questions it seems like an AJAX request would be the most appropriate way to achieve this. I tried to make a function get_employee_name that returns the name of the person based on the way I saw another ajax request worked, but I'm not sure how to implement this so it displays after the # is entered.
My page currently loads, but when I check the network using F12, there is never a call to the function/url that searches for the name to display it on the page. I'm not sure where I might be missing the part that connects these two areas of the code, but I have a feeling it has to do with the html tag where the call is supposed to happen, as I am not too familiar with html and Django.
models.py
class EmployeeWorkAreaLog(TimeStampedModel, SoftDeleteModel, models.Model):
employee_number = models.ForeignKey(Salesman, on_delete=models.SET_NULL, help_text="Employee #", null=True, blank=False)
work_area = models.ForeignKey(WorkArea, on_delete=models.SET_NULL, null=True, blank=False)
station_number = models.ForeignKey(StationNumber, on_delete=models.SET_NULL, null=True, blank=True)
This is the model where the name is stored
alldata/models.py
class Salesman(models.Model):
slsmn_name = models.CharField(max_length=25)
id = models.IntegerField(db_column='number', primary_key=True)
I was reading I can add to the "attrs" in the widget an 'onchange' part, but I am not too familiar with how to approach this and tying it to the ajax request from forms and not the html.
forms.py
class WarehouseForm(AppsModelForm):
class Meta:
model = EmployeeWorkAreaLog
widgets = {
'employee_number': ForeignKeyRawIdWidget(EmployeeWorkAreaLog._meta.get_field('employee_number').remote_field, site, attrs={'id':'employee_number_field'}),
}
fields = ('employee_number', 'work_area', 'station_number')
views.py
def enter_exit_area(request):
form = WarehouseForm(request.POST or None)
if form.is_valid():
# Submission stuff/rules
return render(request, "operations/enter_exit_area.html", {
'form': form,
})
def get_employee_name(request):
employee_number = request.GET.get('employee_number')
try:
employee = Salesman.objects.get(id=employee_number)
except Salesman.DoesNotExist:
return JsonResponse({'error': 'Employee not found'}, status=404)
employee_name = employee.slsmn_name
return JsonResponse({'employee_name': employee_name})
urls.py
urlpatterns = [
url(r'enter-exit-area/$', EnterExitArea.as_view(), name='enter_exit_area'),
path('get-employee-name/', views.get_employee_name, name='get_employee_name'),
]
The ajax request I tried to create is at the end of this html. I modified a similar request I found, but it does not actually display anything on the screen, not sure if I'm missing an area where the request is actually never being called, as I am not too familiar with how these types of requests work.
enter_exit_area.html
{% extends "base.html" %}
{% block main %}
<form id="warehouseForm" action="" method="POST" novalidate >
{% csrf_token %}
<div>
<h1 id="employee_name">{{ employee_name }}</h1>
<div>
{{ form.employee_number.help_text }}
{{ form.employee_number }}
</div>
<div>
{{ form.work_area.help_text }}
{{ form.work_area }}
</div>
</div>
<div>
<div>
<button type="submit" name="enter_area" value="Enter">Enter Area</button>
</div>
</div>
</form>
<script>
$("#id_employee_number").change(function () {
var employee_number = $(this).val();
var url = $("#warehouseForm").data("employee-name");
$.ajax({
url: url,
type:'GET',
data: {
'id': employee_number
},
success: function (data) {
var employee_name = data['employee_name'];
$('#employee_name').text(employee_name);
},
error : function (data) {
var error_message = data['error'];
$('#employee_name').text(error_message);
}
});
});
</script>
{% endblock main %}
What could be causing nothing to render on the page? Is there a call missing in the html portion? I don't know if <h1 id="employee_name">{{ employee_name }}</h1> is the proper way to call the function for display.
Well, you don't need AJAX for that, if all you want is just sync name on same page, you can do something like this.
// find elements
var banner = $("#employee_detail")
var input = $("#myTextbox")
// handle click and add class
input.keyup(function() {
banner.html(input.val());
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#id_employee_number {
background: red;
border-radius: 4px;
padding: 20px;
font-size: 25px;
text-align: center;
transition: all 0.2s;
margin: 0 auto;
width: 300px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="id_employee_number">
<p>Employee name</p>
<input type="text" id="myTextbox" />
<br />
<span id="employee_detail">
</span>
</div>
I hope that helps.
Update
I was just trying to explain how AJAX could work out for you. However, given your circumstances, in stead of doing.
input.keyup(function() {
banner.html(input.val());
})
you can do something like this.
input.keyup(function() {
$.get("/get-employee-name", function(data, status) {
employee = JSON.parse(data);
banner.html(employee.employee_name);
});
})
Lemme know, if that helps. and my apologies for responding very late, hope am not too late.
I'm new to coding and django and I'm struggling to find the solution to the following problem having reviewed the answers I've found.
Im creating a search form with multiple fields. When the user selects the first field category (and before hitting search) I would like to dynamically change the queryset for the second field sub_category such that only related values are shown.
I have models.py as follows:
class Product(models.Model):
category = models.ForeignKey("Category")
sub_category = models.ForeignKey("SubCategory")
class Category(models.Model):
name = models.CharField(max_length=256)
class SubCategory(models.Model):
category = models.ForeignKey("Category")
name = models.CharField(max_length=256)
And my forms.py includes:
class BasicSearchForm(forms.Form):
category = forms.ModelChoiceField(
label='Category',
queryset=Category.objects.all(),
to_field_name="name",
empty_label=None,
initial="Red")
sub_category = forms.ModelMultipleChoiceField(
required=False,
label='Type',
queryset= SubCategory.objects.all(),
to_field_name="name",
widget=forms.Select)
And my views.py includes:
def search(request):
if request.method == 'POST':
form = BasicSearchForm(request.POST)
if form.is_valid():
category = form.cleaned_data['category']
sub_category = form.cleaned_data['sub_category']
return render(request, 'myapp/search.html', {'form': form})
else:
form = BasicSearchForm()
return render(request, 'myapp/search.html', {'form': form})
And finally the search.html includes:
<form class="search-form" role="search" action="/search/" method="get">
{{ form }}
<input type="submit" value="Search" />
</form>
I've played around with a few answers but nothing seems to work. I'd really appreciate some help. Thanks in advance!
Update:
Thanks for the feedback. As a result I updated the following:
In my urls.py:
urlpatterns = [
url(r'^ajax/update_subcategories/$', views.update_subcategories, name='update_subcategories'),
And in my views.py:
def update_subcategories(request):
category = request.GET.get('category', None)
sub_category = list(SubCategory.objects.filter(category__name__exact=category).values('name'))
return JsonResponse(sub_category, safe=False)
And I have this in my myapp/search.html:
{% block javascript %}
<script>
$("#id_category").change(function () {
var category = $(this).val();
$.ajax({
url: '{% url "myapp:update_subcategories" %}',
data: {
'category': category,
},
success: function (response) {
var new_options = response;
alert(new_options[0].name); // works
$('#id_sub_category').empty();
$.each(new_options, function(key, value) {
$('#id_sub_category')
.append($('<option>', { value : key })
.text(value.name));
});
}
});
</script>
{% endblock %}
Update: The sub_category options were showing as [object Object] until I changed value to value.name and it looks like it's working. I'll test it out and close unless there are any comments.
Update: Im still having an issue with the browser back button. When a user clicks back the dropdown values have changed back to the original queryset rather than the updated version.
You can't do this from Django views side, ie, backend. You could try an ajax request for implementing this kind of requests, by sending a GET request to the server for populating the drop-down or whatever you are into.
For a simple example, you could refer
here
How do I POST with jQuery/Ajax in Django?
EDIT
def update_subcategories(request):
category = request.GET.get('category', None)
sub_category = list(SubCategory.objects.filter(category__name__exact=category).values('name'))
return JsonResponse(dict(sub_category=sub_category))
Then in ajax response you could grab it like response.data.sub_category
Use ajax to send the category and retrieve subcategory elements.
For the category, send it via get request, and using the orm return the subcategories in a json format which you can show using jQuery.
I'm learning django/python/css/etc... and while doing this, I've decided to build an app for my website that can pull simple movie data from TMDb. What I'm having trouble with is figuring out a way to add a way for the user to select two different movies, and once selected, see the differences between them (run time, budget, etc).
I've got grabbing the data from the API covered in that doing a search for a movie on my site returns expected results. But now I'm having a really tough time trying to figure out how to select 1 item from the results to "save" it, search again, select the second movie, and have the comparison show up.
I know it's pretty vague, but any help getting me pointed in the right direction would be greatly appreciated!
here's what I'm doing so far with the code:
views.py:
from django.shortcuts import render
from django.conf import settings
from .forms import MovieSearch
import tmdbsimple as tmdb
tmdb.API_KEY = settings.TMDB_API_KEY
def search_movie(request):
"""
Search movie title and return 5 pages of results
"""
parsed_data = {'results': []}
if request.method == 'POST':
form = MovieSearch(request.POST)
if form.is_valid():
search = tmdb.Search()
query = form.cleaned_data['moviename']
response = search.movie(query=query)
for movie in response['results']:
parsed_data['results'].append(
{
'title': movie['title'],
'id': movie['id'],
'poster_path': movie['poster_path'],
'release_date': movie['release_date'][:-6],
'popularity': movie['popularity'],
'overview': movie['overview']
}
)
for i in range(2, 5 + 1):
response = search.movie(query=query, page=i)
for movie in response['results']:
parsed_data['results'].append(
{
'title': movie['title'],
'id': movie['id'],
'poster_path': movie['poster_path'],
'release_date': movie['release_date'][:-6],
'popularity': movie['popularity'],
'overview': movie['overview']
}
)
context = {
"form": form,
"parsed_data": parsed_data
}
return render(request, './moviecompare/movies.html', context)
else:
form = MovieSearch()
else:
form = MovieSearch()
return render(request, './moviecompare/compare.html', {"form": form})
def get_movie(request, movid):
"""
from search/movie results, get details by movie id (movid)
"""
movie = tmdb.Movies(movid)
response = movie.info()
context = {
'response': response
}
return render(request, './moviecompare/detail.html', context)
movies.html:
{% extends 'moviecompare/compare.html' %}
{% block movies_returned %}
<div class="wrap">
<div class="compare-gallery">
{% for key in parsed_data.results|dictsortreversed:'release_date' %}
{% if key.poster_path and key.release_date and key.title and key.overview %}
<div class="gallery-item">
<img src="http://image.tmdb.org/t/p/w185/{{ key.poster_path }}">
<div class="gallery-text">
<div class="gallery-date"><h5><span><i class="material-icons">date_range</i></span> {{ key.release_date }}</h5></div>
<div class="gallery-title"><h3>{{ key.title }}</h3></div>
<div class="gallery-overview">{{ key.overview|truncatechars:80 }}</div>
</div>
</div>
{% endif %}
{% endfor %}
</div>
</div>
{% endblock %}
and I've got a simple detail.html that doesn't do anything just yet, but it's just for showing detail results for a single movie, so not important to get into yet as it'll just be styling.
I'd like for each result in the gallery to have a link to a details page for the movie(done), and also a select box (or similar) to select it as one of the comparison movies.
If I can just get some help on how to select two different movies from the search results, and compare those, I think I could work out a way to do the same with two separate movie searches. Thanks for any help!
edit: here's what I have so far on pythonanywhere -
Assuming you want to be able to add movies from different searches (e.g. search for rambo, add rambo, search for south park, add south park), you can probably do something like:
#require_POST
def save_compare(request, pk):
""" Async endpoint to add movies to comparison list """
movies = request.session.get('comparing_moviews', [])
movies.append(movies)
# if you only need the data the API served originally, you could also save that to a data-base when you read it from the API and use it here
request.session['comparing_movies'] = movies
return JsonResponse("")
def show_comparison(request):
""" show differing stats like "1 hour 3 mins longer """
mov1, mov2 = request.session.get('comparing_movies') # need to handle wrong number of selected
# not sure from the description how to look up the movies by id
mov1 = tmdb.Search().search(pk=mov1)
mov2 = tmdb.Search().search(pk=mov2)
return render_to_response("comparison.html", context={
"mov1": mov1, "mov2": mov2,
"length_diff": mov1['length'] - mov2['length']})
to give you the comparison stats you want. You'll also need session middleware installed.
From the search results page, you'll add a button that triggers an async js post to save_compare which starts accumulating a comparison list. When they're done, they can click "compare" or something on that list, which will render "comparison.html" with the two movies and the diff however you like it.
As an example of some calculation for your "comparison.html", you can grab the hours and minutes from the length_diff and render them as "X hours and y minutes longer."
I need to implement two dropdown lists that the values of the seconds depends on the selection of the first.
I was able to implement that in the backend but I am struggling to do it in the front end and more specifically with javascript!
countries = Country.objects.filter(Enabled=True)
citiesByCountry = {}
for country in countries:
citiesInCountry = City.objects.filter(Enabled=True, Country=country)
cities = []
for city in citiesInCountry:
cities.append(city.Name)
citiesByCountry[country.Name] = cities
context = {'citiesByCountry': citiesByCountry}
return render(request, 'index.html', context)
So I have the following structure:
'Country':['City1', 'City2']
Here is the HTML:
<div class="form-group col-md-4">
<select class="form-control" onchange="test(this.value)" id="sel1">
{% for country in citiesByCountry %}
<option value="{{ country }}">{{ country }}</option>
{% endfor %}
</select>
</div>
<div class="form-group col-md-4">
<select class="form-control" id="cities">
</select>
</div>
So I have added the following javascript:
<script>
var country_objs = {};
{% for country, cities in citiesByCountry.items %}
country_objs['{{country|escapejs}}'] = '{{cities|escapejs}}';
{% endfor %}
</script>
<script type="application/javascript">
function test(country) {
var $cities_select = $("#cities");
$(country_objs[country]).each(function(){
$cities_select.append('<option>' + this + '<\option>');
});
}
</script>
The second dropdown never get populated but when I print the contents of the country_objs like this: console.log(country_objs[country]);
I get the following:
['City1', 'City2', 'City3']
Which is correct, but the .each function does not loop through the items. I think the problem is that the above is not a proper array but a string but still can't understand why.
Note that I get the following error:
jquery.min.js:2 Uncaught Error: Syntax error, unrecognized expression: ['City1', 'City2', 'City3']
Unfortunately whatever I try won't work, I couldn't imagine that implementing this in Django will be so hard.
I would like to avoid using a third-party app or module to do this simple thing and I would like to use a proper way to do it (i.e the best way) so any ideas will be really valuable.
There are two solutions:
Solution 1:
use a for loop:
country_objs['{{country|escapejs}}'] = [{% for city in cities %}"city",{% endfor %}];
Solution 2:
Switch the line:
citiesByCountry[country.Name] = cities
for:
citiesByCountry[country.Name] = json.dumps(cities)
to encode to json, and then in the template:
country_objs['{{country|escapejs}}'] = {{cities|safe}};
Obs regarding solution 2:
You can't have the single quotes around the variable
'{{cities|safe}}';
in the second solution, or else when you add the list ['City1', 'City2', 'City3'] you're gonna have:
'['City1', 'City2', 'City3']'
I think you want to remove the |escapejs filter for the part you want to be parsed in JavaScript. You might even find you need |safe, but you should be certain that you have control over what gets output there before considering that.
var country_objs = {};
{% for country, cities in citiesByCountry.items %}
country_objs['{{country|escapejs}}'] = {{cities|safe}};
{% endfor %}
For the updating part, this should work:
function updateCities(country) {
var $cities_select = $("#cities");
$(country_objs[country]).each(function(key, value) {
$('#cities').append($("<option></option>")
.attr("value",key)
.text(value));
});
}
$('#sel1').change(function() {
updateCities(this.value);
});
Credit due in part to this answer https://stackoverflow.com/a/171007/823020.
The above is missing an initial setting, which you could either do in templating or JavaScript. For JavaScript, you could insert another updateCities($('#cities).val());.
The above also appends every time, instead of resetting the options to an empty list. This is left as an exercise for the reader.
Suggestion 1: You didn't discuss this, but your initial query would be better done something like this:
# the following may differ based on your foreign key's related_name attribute
countries = Country.objects.filter(Enabled=True).select_related('city_set')
for country in countries:
citiesInCountry = country.city_set.values_list('name', flat=True)
This would all be a single query. However you'd need to rethink about the 'active' flag, or how to achieve that if you still need it.
Suggestion 2: To be honest, it might be better in general to wrap it up in json. In your view:
import json
countries_json = json.dumps(citiesByCountry)
and then in the template:
var country_objs = {{ citiesByCountry|safe }};
Using Django 1.6, django-select2 (latest) and jquery.formset.js (latest), I'm struggling with something that should be quite simple. Essentially when I use the add formset capability provided by jquery.formset.js the new formset is missing the select field provided by django-select2, although the rest of the formset renders fine.
It's a fairly basic set up:
class PartNumber(models.Model):
name = models.CharField("Description", max_length=100)
supplier_part_number = models.CharField(max_length=30, unique=True, blank=True, null=True)
class PurchaseOrder(models.Model):
po_number = models.CharField('PO number', max_length=10, unique=True)
ordered_date = models.DateField(default=today)
class PurchaseOrderPart(models.Model):
part_number = models.ForeignKey(PartNumber, related_name='purchases')
po_number = models.ForeignKey(PurchaseOrder, related_name='partslist')
delivery_date = models.DateField(null=True, blank=True)
qty_ordered = models.IntegerField('Quantity ordered',validators=[MinValueValidator(1)])
cost = models.DecimalField('Unit Cost', max_digits=10,decimal_places=2,blank=True,null=True)
I have the create view of a PurchaseOrder having PurchaseOrderParts as an inline_formset:
class PurchaseOrderPartForm(forms.ModelForm):
part_numbers = PartNumberChoices()
class Meta:
fields = ('part_numbers', 'delivery_date', 'qty_ordered', 'cost')
model = PurchaseOrderPart
widgets={
'part_numbers': forms.Select(attrs={'class':'form-control'}),
'delivery_date': CalendarWidget(attrs={'class':'input-append form-control'}),
'qty_ordered': forms.NumberInput(attrs={'class':'form-control'}),
'cost': forms.NumberInput(attrs={'class':'form-control'}),
}
POPartFormset = inlineformset_factory(PurchaseOrder, PurchaseOrderPart, form=PurchaseOrderPartForm, extra=1, can_delete=True)
And I'm using jquery.formset.js so that there are "add" and "remove" buttons on the PurchaseOrder create view, so that any number of PurchaseOrderParts might be added (any one PO might have from 1 to infinite parts attached, in reality more likely to be < 10).
That was working fine.
But the PartNumber fk on a PurchaseOrderPart has a huge list (~2500 items) so the select list is inconvenient.
I installed django-select2 and was pretty impressed at how easily it was to get running:
class PurchaseOrderPartForm(forms.ModelForm):
part_numbers = PartNumberChoices()
class Meta:
fields = ('part_numbers', 'delivery_date', 'qty_ordered', 'cost')
model = PurchaseOrderPart
widgets={
'part_numbers': django_select2.AutoHeavySelect2Widget(),
'delivery_date': CalendarWidget(attrs={'class':'input-append form-control'}),
'qty_ordered': forms.NumberInput(attrs={'class':'form-control'}),
'cost': forms.NumberInput(attrs={'class':'form-control'}),
}
This is when we have problems. It all works until you use the jquery.formset.js to add a new formset - it renders perfectly but fails to include the django-select2 select.
In particular, the new formset is missing this code:
<div class="select2-container select2-container-active" id="s2id_id_partslist-1-part_numbers"> <span class="select2-chosen"> </span><abbr class="select2-search-choice-close"></abbr> <span class="select2-arrow"><b></b></span><input class="select2-focusser select2-offscreen" type="text" id="s2id_autogen5"></div>
How might I go about force inserting the select2-container?
This is a known bug of django-select2 that has been fixed in a very recent merge.
When you are saying that you are running the latest version, do you mean from github or from PIP ?
Either way, you might be interrested in this:
https://github.com/applegrew/django-select2/pull/127