Django - Автозаполнение поиска выбрасывает ошибку для зарегистрированных пользователей?
Я делаю некоторые обновления в части автозаполнения функциональности поиска в моем приложении, и по какой-то причине я получаю ошибку для вышедших из системы пользователей, которая говорит TypeError: Field 'id' expected a number but got <SimpleLazyObject: <django.contrib.auth.models.AnonymousUser object at 0x1088a5e20>>.
Это происходит только для пользователей, вышедших из системы. Когда пользователь вошел в систему, автозаполнение работает, как и было задумано, поэтому я знаю, что я что-то упускаю, но я просто не знаю что.
Что нужно изменить, чтобы решить эту проблему?
Я создаю игровое приложение, в котором пользователи имеют доступ к игре в определенные игры на основе их ранга в нашем приложении. Поэтому, когда пользователь входит в систему, я хотел бы, чтобы функция автозаполнения отражала игры, которые он разблокировал.
Так, скажем, если у пользователя ранг rank
составляет 100
, то будут отображаться все игры с рангом game_rank
от 100 и ниже.
Для пользователей, вышедших из системы, я бы хотел, чтобы отображались все игры.
Сделал несколько заметок в моем views.py
коде из того, что я тестировал, и добавил JavaScript в функцию поиска на всякий случай.
Ниже приведены скриншоты того, что отображается для пользователей, вышедших из системы и вошедших в нее.
Снимок экрана залогиненного пользователя
Ниже приведен мой код.
Любая помощь будет принята с радостью!
Всех благ!
models.py
class Game_Info(models.Model):
id = models.IntegerField(primary_key=True, unique=True, blank=True, editable=False)
game_title = models.CharField(max_length=100, null=True)
game_rank = models.IntegerField(default=1)
game_image = models.ImageField(default='default.png', upload_to='game_covers', null=True, blank=True)
class User_Info(models.Model):
id = models.IntegerField(primary_key=True, blank=True)
image = models.ImageField(default='/profile_pics/default.png', upload_to='profile_pics', null=True, blank=True)
user = models.OneToOneField(settings.AUTH_USER_MODEL,blank=True, null=True, on_delete=models.CASCADE)
rank = models.IntegerField(default=1)
views.py
def search_results_view(request):
if request.headers.get('x-requested-with') == 'XMLHttpRequest':
res = None
game = request.POST.get('game')
print(game)
## This works for both logged in and logged out users but inlcudes all games. Would like to have this for logged out users. Commenting this out to test below.
# qs = Game_Info.objects.filter(game_title__icontains=game).order_by('game_title')[:4]
## This only works for when users are logged in.
user_profile_game_obj = User_Info.objects.get(user=request.user)
user_profile_rank = int(user_profile_game_obj.rank)
qs = Game_Info.objects.filter(game_title__icontains=game, game_rank__lte=user_profile_rank).order_by('game_title')[:4]
if len(qs) > 0 and len(game) > 0:
data = []
for pos in qs:
item ={
'pk': pos.pk,
'name': pos.game_title,
'game_provider': pos.game_provider,
'image': str(pos.game_image.url),
'url': reverse('detail', args=[pos.pk]),
}
data.append(item)
res = data
else:
res = 'No games found...'
return JsonResponse({'data': res})
return JsonResponse({})
custom.js
// Live Search Functionalty
$(function () {
const url = window.location.href
const searchForm = document.getElementById("search-form");
const searchInput = document.getElementById("search_input_field");
const resultsBox = document.getElementById("search-results");
const csrf = document.getElementsByName('csrfmiddlewaretoken')[0].value
const sendSearchData = (game) =>{
$.ajax ({
type: 'POST',
url: '/games/',
data: {
'csrfmiddlewaretoken': csrf,
'game' : game,
},
success: (res)=> {
console.log(res.data)
const data = res.data
if (Array.isArray(data)) {
resultsBox.innerHTML = `<div class="search_heading"><h1>Recommended Games</h1></div>`
data.forEach(game=> {
resultsBox.innerHTML += `
<a href="${game.url}" class="item" >
<div class="row">
<div class="search-cover-container">
<img src="${game.image}" class="game-img">
</div>
<div class="search-title-container">
<p>${game.name}</p>
<span class="publisher_title">${game.game_provider}</span>
</div>
<div class="search-icon-container">
<i class="material-icons">trending_up</i>
</div>
</div>
</a>
`
})
} else {
if (searchInput.value.length > 0) {
resultsBox.innerHTML = `<h2>${data}</h2>`
} else {
resultsBox.classList.add('not_visible')
}
}
},
error: (err)=> {
console.log(err)
}
})
}
searchInput.addEventListener('keyup', e=> {
sendSearchData(e.target.value)
})
});
base.html
<form method="POST" autocomplete="off" id="search-form" action="{% url 'search_results' %}">
{% csrf_token %}
<div class="input-group">
<input id="search_input_field" type="text" name="q" autocomplete="off" class="form-control gs-search-bar" placeholder="Search Games..." value="">
<div id="search-results" class="results-container not_visible"></div>
<span class="search-clear">x</span>
<button id="search-btn" type="submit" class="btn btn-primary search-button" disabled>
<span class="input-group-addon">
<i class="zmdi zmdi-search"></i>
</span>
</button>
</div>
</form>
Исходя из запроса, который вы показываете, пользователи, вышедшие из системы, не должны даже переходить к этому представлению. Я бы украсил представление символами @login_required
.
Проблема, которую вы наблюдаете, находится в этой строке:
user_profile_game_obj = User_Info.objects.get(user=request.user)
Когда пользователь не вошел в систему, request.user
является экземпляром AnonymousUser
, у которого нет идентификатора. При выполнении этого запроса базовый фреймворк получает идентификатор переданного экземпляра, который в данном случае не может быть разрешен. Вы не можете связать AnonymousUser
с записью, у них нет идентификатора, и их нельзя использовать в запросах.
Альтернативно, вы можете изменить поведение представления, используя user.is_authenticated()
Как уже было сказано @Sam Morgan, request.user
- это AnonymousUser
. Добавление @login_required
не поможет, но использование user.is_authenticated()
должно сработать:
def search_results_view(request):
if request.headers.get('x-requested-with') == 'XMLHttpRequest':
res = None
game = request.POST.get('game')
print(game)
## This works for both logged in and logged out users but inlcudes all games. Would like to have this for logged out users. Commenting this out to test below.
# qs = Game_Info.objects.filter(game_title__icontains=game).order_by('game_title')[:4]
## This only works for when users are logged in.
if request.user.is_authenticated:
user_profile_game_obj = User_Info.objects.get(user=request.user)
user_profile_rank = int(user_profile_game_obj.rank)
qs = Game_Info.objects.filter(game_title__icontains=game, game_rank__lte=user_profile_rank).order_by('game_title')[:4]
else:
qs = Game_Info.objects.filter(game_title__icontains=game).order_by('game_title')[:4]
if len(qs) > 0 and len(game) > 0:
data = []
for pos in qs:
item ={
'pk': pos.pk,
'name': pos.game_title,
'game_provider': pos.game_provider,
'image': str(pos.game_image.url),
'url': reverse('detail', args=[pos.pk]),
}
data.append(item)
res = data
else:
res = 'No games found...'
return JsonResponse({'data': res})
return JsonResponse({})