Check if requested user is owner in django - javascript

I have developed an app which is about storing hotel information. I want an edit and delete feature too but edit button should be visible only to that user who has registered hotel not to all the authenticated user. I want to pass if requested user is the one who has registered hotel through javascript argument. How can i check this?
hotel_detail.html
<script type="text/javascript">
var data = {
pk:{{ instance.pk }},
isUserAuthenticated:{% if request.user.is_authenticated %}true{% else %}false{% endif %},
user:'{{request.user.username}}'
}
console.log('owner is', data.user);
console.log(data);
$(function() {
app.showRoomDetail("hotelDetail",data);
});
</script>
views
def hotel_detail(request, pk):
instance = get_object_or_404(Hotel, pk=pk)
context = {
'pk':instance.pk,
'instance':instance
}
return render(request,'rentals/hotel_detail.html',context)
Models
class Hotel(models.Model):
ownerName = models.CharField(_("Owner's Name"),max_length=255, blank=True,null=True,
help_text=_("Owner's Full Name"))
email = models.CharField(max_length=120,blank=True,null=True)
phoneNumber = models.PositiveIntegerField(blank=False,null=True,
help_text=_("Phone number of contact person"))
hotelName = models.CharField(_("Hotel Name"), max_length=255, blank=False,null=True)
slug = models.SlugField(unique=True,blank=True,null=True)
summary = models.TextField(max_length=500, blank=True,null=True,help_text=_("Description of the Hotel"))
location = models.CharField(_("location"),max_length=10,null=True)
room = models.PositiveIntegerField(_("No of Rooms"), blank=False, null=True,
help_text=_("Number of bedrooms available"))
price = models.PositiveIntegerField(blank=False,null=True,
help_text=_("Price per room"))

Well what you can do is add user field or say hotelOwner in your models.py like
hotelOwner = models.ForeignKey(User) # note you need to import user model
now in your data object inside javascript you should do following
user:{% if request.user == instance.renter %}true{% else %}false{% endif %}

Related

django: How to write JavaScript fetch for url with slug parameter?

A django and async newbie here, trying to improve a simple message-board app. I'm sure you've all seen this problem dozens of times, but I'm unable to find a solution...
Currently, when a user likes a posted message, it refreshes the whole page. I'd like to use simple JavaScript with the fetch API to prevent this, without having to resort to Ajax, as I've never used it. The problem is, I'm very new to the fetch method as well and I'm struggling to find the correct syntax for the url in the fetch request, as it uses the post model's slug field as a parameter. Like so:
urls.py
urlpatterns = [
...
path('post/<slug:slug>/', views.FullPost.as_view(), name='boards_post'),
path('like/<slug:slug>/', views.PostLike.as_view(), name='post_like'),
...
]
models.py
...
class Post(models.Model):
"""
Model for message posts
"""
STATUS = ((0, "Draft"), (1, "Published"))
title = models.CharField(max_length=200, unique=True)
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="board_posts"
)
category = models.ForeignKey(
Category,
on_delete=models.CASCADE,
default="",
related_name="category_posts"
)
created_on = models.DateTimeField(auto_now_add=True)
updated_on = models.DateTimeField(auto_now=True)
content = models.TextField()
post_image = CloudinaryField('image', default='placeholder')
status = models.IntegerField(choices=STATUS, default=0)
likes = models.ManyToManyField(User, related_name="post_likes")
class Meta:
# Orders posts in descending order
ordering = ['-created_on']
def __str__(self):
return self.title
def number_of_likes(self):
return self.likes.count()
def get_absolute_url(self):
"""
Method to tell django how to find the url to any specific
instance of a post when the Post model is instantiated,
(i.e. a new post created). Returns the url generated by the
'boards_post' path from the FullPost class view, with
this model's slug field as a keyword argument. This
effectively acts as a redirect to the full_post.html template.
"""
return reverse('boards_post', kwargs={'slug': self.slug})
...
views.py
...
class FullPost(View):
"""
View for a single post, selected by the user, displaying
comments and likes. The url for each individual post is derived
from the Post model's slug field which is, in turn,
populated by the title.
"""
def get(self, request, slug, *args, **kwargs):
"""
Method to get post object.
"""
queryset = Post.objects.filter(status=1)
post = get_object_or_404(queryset, slug=slug)
comments = post.comments.order_by('created_on')
liked = False
if post.likes.filter(id=self.request.user.id).exists():
liked = True
return render(
request,
"full_post.html",
{
"post": post,
"comments": comments,
"liked": liked,
"comment_form": CommentForm()
},
)
def post(self, request, slug, *args, **kwargs):
"""
Post method for comment form.
"""
queryset = Post.objects.filter(status=1)
post = get_object_or_404(queryset, slug=slug)
comments = post.comments.order_by("-created_on")
liked = False
if post.likes.filter(id=self.request.user.id).exists():
liked = True
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
comment_form.instance.name = self.request.user
comment = comment_form.save(commit=False)
comment.post = post
comment.save()
else:
comment_form = CommentForm()
return redirect(self.request.path_info)
...
...
class PostLike(View):
"""
View for liking and unliking posts.
"""
def post(self, request, slug):
"""
Method to toggle liked/unliked state on a particular post.
"""
post = get_object_or_404(Post, slug=slug)
liked = True
if post.likes.filter(id=self.request.user.id).exists():
post.likes.remove(request.user)
liked = False
else:
post.likes.add(request.user)
likes = post.number_of_likes()
# ----- The original return statement is the one commented out below:
# return HttpResponseRedirect(reverse('boards_post', args=[slug]))
return JsonResponse({"likes": likes, "liked": liked})
...
Snippet from full post template
...
<div class="row">
<div class="col-1">
{% if user.is_authenticated %}
<strong>
<form class="d-inline" action="{% url 'post_like' post.slug %}" method="POST">
{% csrf_token %}
{% if liked %}
<!-- I used 'post.id' for the argument passed to the like function here, as I couldn't get 'post.slug' to work -->
<button class="btn-like" type="submit" name="post_id" value="{{ post.slug }}" onclick="like({{ post.id }})">
<i id="like-btn" class="fas fa-thumbs-up"></i>
</button>
<span id="likes-count">{{ post.number_of_likes }}</span>
{% else %}
<button class="btn-like" type="submit" name="post_id" value="{{ post.slug }}" onclick="like({{ post.id }})">
<i id="like-btn" class="far fa-thumbs-up"></i>
</button>
<span id="likes-count">{{ post.number_of_likes }}</span>
{% endif %}
</form>
</strong>
{% else %}
<strong class="text-secondary"><i class="far fa-thumbs-up"></i> <span id="likes-count">{{ post.number_of_likes }}</span></strong>
{% endif %}
</div>
...
All I've got so far in JavaScript is the following...
function like(post_id) {
let likeButton = document.getElementById("like-btn");
let likeCount = document.getElementById("likes-count");
console.log(likeCount.innerText);
console.log(post_id);
// -------- I've no idea what works here! I've tried both of the below
// -------- and several variations.
// -------- Obviously, none work.
// const url = "{% url 'post_like' post.slug %}";
//or const url = `/like/${post.slug}`;
fetch(url, {method: "POST"})
.then(response => response.json())
.then(data => {
console.log(data);
// ------ If I ever get the json data, I'll use it here to manipulate the
// ------ likeButton and likeCount variables in the DOM. Something like:
likeCount.innerHTML = data["likes"];
if (data["liked"] === true) {
likeButton.className = "fas fa-thumbs-up";
} else {
likeButton.className = "far fa-thumbs-up";
}
})
.catch((e) => alert("Unable to like/unlike post."));
// -------- or something.
}
Also, I know I need to handle the csrf token somehow but I've no idea how. Like I said, total newbie.
So, can anyone help? And does anyone have any advice?
Cheers!
For csrf_token problem add to view this decorator:
from django.utils.decorators import method_decorator
#method_decorator(csrf_exempt, name='dispatch')
class PostLike(View):
...
For fetch you need to have whole path, not just relative path. This should work:
url = "{{ request.scheme }}://{{ request.get_host }}{% url 'post_like' post.slug %}"

Django: how can i display an image from the django database on a template based on an selected option from a form?

I am a beginner with django and I have been struggling for a few weeks to correlate a user's selected option in a form with the data in my database to list the logo of the selected brand and the car model image in the template.
So far I have managed to bring and store the data from the selected form in the database (MasinaSelectata model), the data is updated according to the client's ip.
Now I need to use the data stored in the (MasinaSelectata model) and list the brand logo in the (Constructor model) and the model image in the (Model model). Which I fail to do due to lack of experience and knowledge.
I tried several variants, none of them succeeded, my last attempt is the one below.
Models:
class Constructor(models.Model):
constructor_nume = models.CharField(max_length=100, unique=True)
slug = models.SlugField(max_length=150, unique=True)
logo_constructor = models.ImageField(upload_to='photos/selectormasina', blank=True)
class Model(models.Model):
constructor = models.ForeignKey(Constructor, on_delete=models.CASCADE)
model_nume = models.CharField(max_length=200, unique=True)
slug = models.SlugField(max_length=250, unique=True)
imagine_model = models.ImageField(upload_to='photos/selectormasina', blank=True)
class Versiune(models.Model):
constructor = models.ForeignKey(Constructor, on_delete=models.CASCADE)
model = ChainedForeignKey(Model, chained_field="constructor", chained_model_field="constructor",
show_all=False,
auto_choose=True,
sort=True)
versiune_nume = models.CharField(max_length=300, unique=True)
slug = models.SlugField(max_length=350, unique=True)
class MasinaSelectata(models.Model):
constructor = models.ForeignKey(Constructor, on_delete=models.CASCADE)
model = models.ForeignKey(Model, on_delete=models.CASCADE)
versiune = models.ForeignKey(Versiune, on_delete=models.CASCADE)
clientip = models.CharField(max_length=20)
Views.py:
def get_client_ip(request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
return ip
def logo(request):
ip = get_client_ip(request)
select = MasinaSelectata.objects.filter(clientip = ip)
context = { 'select': select}
return render(request, 'navbar.html', context)
Templates:
navbar.html
<div class="col-lg-2 col-md-3 col-6 ">
<!-- logo marca-->
<div class="img-fluid">
<img src="{{ select.constructor.logo_constructor.url }}" alt="{{select.logo.constructor.constructor_nume.url}}"></div>
</div>
I researched django documentation, google, youtube, and SO but I don't seem to have found a solution.
I'm sure it's something I'm missing so please help me.
I finally managed to find a solution:
Views.py:
to show the selected manufacturer's logo
def get_imagine_constructor_data(request, *args, **kwargs):
selected_constructor = kwargs.get('producator')
img_constructor =
list(Constructor.objects.filter(constructor_nume=selected_constructor).values())
return JsonResponse({'data':img_constructor})
to show the image of the selected model
def get_imagine_model_data(request, *args, **kwargs):
selected_model = kwargs.get('model')
img_modele = list(Model.objects.filter(model_nume=selected_model).values())
return JsonResponse({'data':img_modele})
Url.py
path('imagine-masina-json/<str:model>/', views.get_imagine_model_data,
name='imagine-masina-json'),
path('imagine-constructor-json/<str:producator>/',
views.get_imagine_constructor_data, name='imagine-constructor-json'),
I used a script to correlate the data in the database with the choices made by the client and show the images corresponding to the selections.
For me it was easier this way.
Templates: navbar.html
<div class="img-fluid" id="imagine-constructor" ></div>
<div class="img-fluid" id="imagine-model" ></div>
const imagineDataBox = document.getElementById('imagine-model')
const imagineConstructorDataBox = document.getElementById('imagine-constructor')
for manufacturer's logo
imagineConstructorDataBox.innerHTML = ""
$.ajax({
type: 'GET',
url: `imagine-constructor-json/${selectedConstructor}/`,
success: function(response){
console.log(response.data)
const logo = response.data
logo.map(item=>{
const option = document.createElement('img')
option.textContent = item.constructor_nume
option.setAttribute('src', `media/${item.logo_constructor}`)
option.setAttribute('alt', item.constructor_nume)
option.setAttribute('style', 'max-height: 150px;')
option.setAttribute('class', 'img-fluid')
imagineConstructorDataBox.appendChild(option)
})
},
error: function(error){
console.log(error)
},
})
for model image:
imagineDataBox.innerHTML = ""
$.ajax({
type: 'GET',
url: `imagine-masina-json/${selectedModel}/`,
success: function(response){
console.log(response.data)
const logo = response.data
logo.map(item=>{
const option = document.createElement('img')
option.textContent = item.model_nume
option.setAttribute('src', `media/${item.imagine_model}`)
option.setAttribute('alt', item.model_nume)
option.setAttribute('style', 'max-height: 200px;')
imagineDataBox.appendChild(option)
})
},
error: function(error){
console.log(error)
},
})
I hope it will be useful for someone else who will run into this problem.

Django 3. Having trouble passing dropdown menu selection from .html to forms

I'm still new to Django (2 weeks or so). I've been struggling the past few days with passing a string from an html file to forms. My project lets the user choose a state from a dropdown menu (Michigan and Ohio for now, I'll add the rest later). When the state is selected, it will take that string and pull a list of counties of that state from a spreadsheet. This is where the problem lies. I've searched far and wide and I just can't seem to find a solution. The major holdback to many of these solutions is I don't want to "submit" with a button. I want the user to "select" a state, then select a county without a page refresh. I've also included a screenshot of the webpage. So far the dependent dropdowns work perfectly thanks to a youtube tutorial. The "submit" button in the picture is cosmetic for now.
Thanks in advance for helping out. Let me know if you have any questions regarding models or anything else regarding the code.
views.py
def StateForm_Page(request):
context = {}
stateChoice = 'Michigan' //hardcode a state so if the post fails, the function can still find an excel sheet
if request.method == 'POST':
State_Form = StateForm(request.POST)
stateChoice = State_Form.cleaned_data['stateChoice'] //I think this is where my code is failing
else:
State_Form = StateForm()
context['State_Form'] = State_Form
dataArray = pd.read_excel(r'C:\filename.xls', sheet_name= stateChoice)
county_strings = dataArray['County '].values.tolist()
json_county_strings = json.dumps(county_strings)
context['json_county_strings'] = json_county_strings
return render(request, 'StateForm_page.html', context)
StateForm_page.html
<body>
<form action="" method="POST" name="stateChoice">
{% csrf_token %}
{{ State_Form.as_p }}
</form>
<script>
var state;
var county;
$(document).ready(function(){
$('#id_county').empty(); //empties county before state is chosen
$("#id_state").on('change', function(){ //when #id_state is changed...
state = $("#id_state").val(); //assign state with the selection
var countyStrings = JSON.parse('{{ json_county_strings | escapejs }}'); //grabs counties from respective state
var length = countyStrings.length;
var i;
for(i=0; i < length; i++){
county = countyStrings[i]; //update county options with spreadsheet values
$('#id_county').append(
`
<option value ="${county}">
${county}
</option>
`
);
}
});
})
}
</script>
</body>
What the webpage looks like so far:
Next day changes
Hey, after putting a few more hours into it, I'm still not having every luck. Per your suggestions, below is what I've added
views.py
def retrieveState(request):
statePick = request.GET.get('state')
return JsonResponse(statePick, safe = False)
def StateForm_Page(request):
context = {}
stateChoice = []
if request.method == 'POST':
#stateChoice = State_Form.cleaned_data['stateChoice']
State_Form = StateForm(request.POST)
stateChoice = retrieveState(request)
else:
stateChoice = 'Michigan'
State_Form = StateForm()
StateForm_Page.html
$.ajax({
type: 'POST',
url: 'ajax/retrieveState/',
data: state,
dataType: 'json',
});
Good news is something is being triggered on the ajax url I added, but I don't think the function in views is retrieving the ajax data. Any suggestions? Thanks for your help!

How can a Ajax/jQuery script show 2+ dependent dropdown box entries within one Form url?

I am developing a simple form prototype that contains 4 entries in PythonAnywhere (Python 3.7 + Django):
PinID (Independent, simple manual number entry)
Region (Independent Dropdown Box)
Name (Region-Dependent Dropdown Box)
Source (Name-Dependent Dropdown Box)
What shows up in the Name box is dependent on the Region box, and what shows up in the Source box is dependent on the Name box. So ultimately, the Source Box is dependent on the Region box (If A, then B. If B, then C. So C is dependent on A). To note, if the Region or Name box is blank, their respective dependents are blank.
As my current code is written (which is likely wrong), I can only get my Name box to autopopulate correctly. The Source box remains blank, but it does indeed autopopulate correctly after I refresh the page. However, I intend to keep the form all in one url. I refer to two other .html files to "insert" themselves into the form's .html file without refreshing the page. In the jQuery script, I put the identical segment of code for the second dependent dropdown box under the success function of the first dependent dropdown box, which might be my issue.
So, I presume my coding issue is initiating two different url references within one jQuery script. Does anyone know if I can correct my jQuery script to reference both urls? I included my relevant Django files for reference and if I am overlooking an error in those particular files.
For reference, I have based my work using this tutorial. However, this tutorial only worked with one dependent dropdown box.
form.html (Ajax/jQuery)
<form method="post" id="nameForm" data-names-url="{% url 'ajax_load_names' %}" data-sources-url="{% url 'ajax_load_sources' %}" novalidate>
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<button type="submit">Add</button>
Nevermind
</form>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
$("#id_region").change(function () {
var url = $("#nameForm").attr("data-names-url");
var regionId = $(this).val();
$.ajax({
url: url,
data: {
'region': regionId
},
success: function (data) {
$("#id_name").html(data);
$("#id_source").change(function () {
var url = $("#nameForm").attr("data-sources-url");
var nameId = $(this).val();
$.ajax({
url: url,
data: {
'name': nameId
},
success: function (data) {
$("#id_source").html(data);
}
});
});
}
});
});
</script>
name_dropdown_list_options.html
<option value="">---------</option>
{% for name in names %}
<option value="{{ name.pk }}">{{ name.name }}</option>
{% endfor %}
source_dropdown_list_options.html
<option value="">---------</option>
{% for source in sources %}
<option value="{{ source.pk }}">{{ source.source }}</option>
{% endfor %}
forms.py
class RequestForm(forms.ModelForm):
class Meta:
model = Request
fields = ('pinid', 'region', 'name', 'source')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['name'].queryset = Name.objects.none()
self.fields['source'].queryset = Source.objects.none()
if 'region' in self.data:
try:
region_id = int(self.data.get('region'))
self.fields['name'].queryset = Name.objects.filter(region_id=region_id).order_by('region')
except (ValueError, TypeError):
pass # invalid input from the client; ignore and fallback to empty Name queryset
elif self.instance.pk:
self.fields['name'].queryset = self.instance.region.name_set.order_by('region')
if 'name' in self.data:
try:
name_id = int(self.data.get('name'))
self.fields['source'].queryset = Source.objects.filter(name_id=name_id).order_by('name')
except (ValueError, TypeError):
pass # invalid input from the client; ignore and fallback to empty Source queryset
elif self.instance.pk:
self.fields['source'].queryset = self.instance.name.source_set.order_by('source')
models.py
class Region(models.Model):
region = models.CharField(max_length=30)
def __str__(self):
return self.region
class Name(models.Model):
region = models.ForeignKey(Region, on_delete=models.CASCADE)
name = models.CharField(max_length=30)
def __str__(self):
return self.name
class Source(models.Model):
name = models.ForeignKey(Name, on_delete=models.CASCADE)
source = models.CharField(max_length=30)
def __str__(self):
return self.source
class Request(models.Model):
pinid = models.PositiveIntegerField()
region = models.ForeignKey(Region, on_delete=models.SET_NULL, null=True)
name = models.ForeignKey(Name, on_delete=models.SET_NULL, null=True)
source = models.ForeignKey(Source, on_delete=models.SET_NULL, null=True)
def __int__(self):
return self.pinid
views.py
class NameCreateView(CreateView):
model = Request
form_class = RequestForm
success_url = reverse_lazy('name_changelist')
def load_names(request):
region_id = request.GET.get('region')
names = Name.objects.filter(region_id=region_id).order_by('region')
return render(request, '/home/name_dropdown_list_options.html', {'names': names})
def load_sources(request):
name_id = request.GET.get('name')
sources = Source.objects.filter(name_id=name_id).order_by('name')
return render(request, '/home/source_dropdown_list_options.html', {'sources': sources})
First move change your form.html javascript, so that the $("#id_source").change(function ()
is on the same level as the name change function. I suppose otherwise it will never be called:
forms.html javascript
<script>
$("#id_region").change(function () {
var url = $("#nameForm").attr("data-names-url");
var regionId = $(this).val();
$.ajax({
url: url,
data: {
'region': regionId
},
success: function (data) {
$("#id_name").html(data);
}
});
});
$("#id_source").change(function () {
var url = $("#nameForm").attr("data-sources-url");
var nameId = $(this).val();
$.ajax({
url: url,
data: {
'name': nameId
},
success: function (data) {
$("#id_source").html(data);
}
});
});
</script>
I would start with a much simpler design to fetch the names and sources. Remove the complete init of your request form so that the standard init gives you all values for the names and sources. You will need them because the form validates against these values and if its empty none of them will be valid. You can remove the values then with a little javascript:
$(document).ready(function() {
$("#id_region").html("---"); // or whatever your null value will be
$("#id_source").html("---");
});
To get the values of names and sources you need to view functions:
def load_names(request, region_id):
names = Name.objects.filter(region__id=region_id).order_by('region')
html = ''
for name in names:
html += '<option value="{id}">{name}</option>\n'.format(id=name.id, name=name.name)
return HttpResponse(html)
def load_sources(request, name_id):
sources = Source.objects.filter(name__id=name_id).order_by('region')
html = ''
for source in sources:
html += '<option value="{id}">{source}</option>\n'.format(id=name.id, source=source.source)
return HttpResponse(html)
If this works fine you can change them to templatetags, if you want
At last you need the proper url definitions:
path('load_names', views.load_names, name='load-names'),
path('load_sources', views.load_sources, name='load-sources'),
Pass these to your view form.

Display intro video first visit only Django

I have a Django app that needs to display a video that gives an into to the website. I only want it to do this on the initial visit as opposed to every time the user refreshes. I feel like sessions would have something to do with this but I’m not sure. Thanks!
I think its best to put this flag directly in your database. You can put a field in your user model(if you are using custom user) or in a model which has OneToOne relation with User. For example:
class Profile(models.Model):
user = models.OneToOneField(User)
has_seen_intro = models.BooleanField(default=False)
And send this information to Template from view like this, for example:
class HomeView(TemplateView):
template_name = 'home.html'
def get_context_data(self, **kwargs):
context = super(HomeView, self).get_context_data(**kwargs)
profile = self.request.user.profile
if not profile.has_seen_intro:
context['show_intro'] = True
profile.has_seen_intro = False
profile.save()
# or use user.has_seen_intro if you have custom model
return context
And update the template like this
{% if show_intro %}
// intro video codes
{% endif %}
Update
for anonymous user, please try like this:
class HomeView(TemplateView):
template_name = 'home.html'
def get_context_data(self, **kwargs):
context = super(HomeView, self).get_context_data(**kwargs)
if self.request.user.is_authenticated:
profile = self.request.user.profile
if not profile.has_seen_intro:
context['show_intro'] = True
profile.has_seen_intro = False
profile.save()
else:
if not self.request.session.get('has_seen_intro', True):
self.request.session['has_seen_intro'] = False
context['show_intro'] = True
return context

Categories