Страницы

Поиск по вопросам

Показаны сообщения с ярлыком django-rest-framework. Показать все сообщения
Показаны сообщения с ярлыком django-rest-framework. Показать все сообщения

воскресенье, 29 марта 2020 г.

Как реализовать систему лайкинга на постах и в дальнейшем обрабатывать их? [закрыт]

#api #django_rest_framework


        
             
                
                    
                        
                            Закрыт. Данный вопрос необходимо конкретизировать. Ответы
на него в данный момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Переформулируйте вопрос,
чтобы он был сосредоточен только на одной проблеме, отредактировав его.
                        
                        Закрыт 9 месяцев назад.
                                                                                
           
                
        
Всем привет! Стоит такая задача - 
В API есть модель "Post". В ней есть пару полей из разряда "id", "price", "name",
"description" и т.д. 
Необходимо реализовать систему лайкинга. То есть когда зарегистрированный пользователь
на клиенте(в данном случае, мобильное приложение) нажимает на кнопочку лайка, срабатывал
какой-то функционал лайкинга. Также необходимо, чтобы через API можно было достучаться
к этим "лайкнутым" постам.
Не знаю как это можно реализовать, чтобы в дальнейшем не перелопачивать всю API,
когда потребуется всё сделать по-человечески...

Пользователь стучится ко всем постам по такому URL - url.com/post/all
К конкретному, с большим кол-вом данных url.com/post/detail/1 - где "1" это ID поста

Есть ли какие-то примеры, best practices? Никак не могу найти.
    


Ответы

Ответ 1



Модель Для начала Вам потребуется создать модель. Предлагаю использовать ContentType. Это позволит связывать наш лайк сразу с несколькими моделями, что очень удобно в свою очередь. models.py: from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import GenericRelation from django.contrib.contenttypes.fields import GenericForeignKey class Like(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='likes') content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.PositiveSmallIntegerField() content_object = GenericForeignKey('content_type', 'object_id') class Post(models.Model): likes = GenericRelation(Like) @property def total_likes(self): return self.likes.count() API Предлагаю сделать like методом для нашей модели. Но перед этим нам нужно подготовить несколько скриптов. services: from django.contrib.contenttypes.models import ContentType from core.models import Like from django.contrib.auth.models import User def add_like(obj, user): obj_type = ContentType.objects.get_for_model(obj) like, is_created = Like.objects.get_or_create(content_type=obj_type, object_id=obj.id, user=user) return like def remove_like(obj, user): obj_type = ContentType.objects.get_for_model(obj) Like.objects.filter(content_type=obj_type, object_id=obj.id, user=user).delete() def is_fan(obj, user) -> bool: obj_type = ContentType.objects.get_for_model(obj) likes = Like.objects.filter(content_type=obj_type, object_id=obj.id, user=user) return likes.exists() def get_fans(obj): obj_type = ContentType.objects.get_for_model(obj) return User.objects.filter(likes__content_type=obj_type, likes__object_id=obj.id) mixins: from rest_framework.decorators import action from rest_framework.response import Response from core.api import services from core.api.serializers import FanSerializer class LikedMixin: @action(detail=True, methods=['post']) def like(self, request, pk=None): obj = self.get_object() services.add_like(obj, request.user) return Response() @action(detail=True, methods=['post']) def unlike(self, request, pk=None): obj = self.get_object() services.remove_like(obj, request.user) return Response() @action(detail=True, methods=['get']) def get_fans(self, request, pk=None): obj = self.get_object() fans = services.get_fans(obj) serializer = FanSerializer(fans, many=True) return Response(serializer.data) serializers: class FanSerializer(serializers.ModelSerializer): full_name = serializers.SerializerMethodField() class Meta: model = User fields = ( 'username', 'full_name', ) @staticmethod def get_full_name(obj): return obj.get_full_name() Не забудьте обновить поля в serializers также для модели Вашей записи и после этого можно приступать к финальной части. Представление Наследуемся от недавно созданного mixin и радуемся! views.py: class PostViewSet(LikedMixin, viewsets.ModelViewSet): ... Теперь у нас есть несколько новых API методов для нашей записи. Примечание: не забудьте, что метод like требует POST запроса, это может ввести Вас в заблуждение Наглядности все равно не хватает, поэтому вот исходники с некоторым окружением.

воскресенье, 26 января 2020 г.

Как в Django REST, AngularJs выполнить удаление не по id?

#django #angularjs #django_rest_framework


В urls.py 

url(r'^someurl/(?P\d+)/', MyApi.as_view(), name='delete')


где name='delete' - это имя функции 

Т.е. URL будет вида 

...someurl/123/


В views.py 

def delete(self, request, pk, format=None):
item = self.model.objects.get(pk=pk) 
item.delete() 
print(pk) 
return Response(status=status.HTTP_204_NO_CONTENT)


В контроллере вызываю 

$http.delete('someurl/' + id + '/');


При таком запросе удалится объект с номером = id

Но сейчас мне нужно удалить объект, не зная его id. Я знаю только значение поля image_url. 

Вызывать 

$http.delete('someurl/1/');


где 1 - это любое число, ни с чем не связанное, т.к. delete требует такого формата
вызова;
и передавать image_url в качестве дополнительного параметра в request? Таким образом:

$http.delete('someurl/1/', {'image_url': 'какой-то урл'});


Есть ли более элегантное решение, чем приписывать магическое число в конце?

Обновление:

При использовании 

$http.delete('someurl/1/', {'image_url': 'какой-то урл'});


и в view.py

def delete(self, request, pk, format=None):
    image_url = request.data['image_url']
    item = self.model.objects.get(image_url=image_url)
    item.delete()
    return Response(status=status.HTTP_204_NO_CONTENT)


Возникает ошибка 403.

Обновление 2

class SortedItemsAPI(mixins.UpdateModelMixin, generics.ListCreateAPIView):
model = SortedItem

permission_classes = [
    permissions.IsAuthenticated
]

def get_serializer_class(self):
    if self.request.method in ['PUT', 'POST']:
        return SortedItemIdSerializerBasic
    return SortedItemSerializer

def get_queryset(self):
    return self.model.objects.filter(user=self.request.user).order_by('order')

def create(self, request, *args, **kwargs):
    data = {'user': request.user.id}
    data.update(request.data)
    serializer = self.get_serializer(data=data)

    serializer.is_valid(raise_exception=True)
    self.perform_create(serializer)
    headers = self.get_success_headers(serializer.data)
    return Response(serializer.data, status=status.HTTP_201_CREATED,
                    headers=headers)

def put(self, request, *args, **kwargs):
    for order, element in enumerate(request.data['sorted']):
        self.model.objects.filter(
            user=request.user, id=element).update(
            order=order
        )
    print(request.data['sorted'])
    return Response(status=status.HTTP_200_OK)

def delete(self, request, pk, format=None):
    item = self.model.objects.get(item_id=pk)
    item.delete()
    return Response(status=status.HTTP_204_NO_CONTENT)


Если меняю на 

 def delete(self, request, pk, format=None):
        item = self.model.objects.get(item_id=request.GET['id'])
        item.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)


и, конечно, передаю в request.data параметр id, который в точности равен pk - возникает
ошибка 500 INTERNAL SERVER ERROR.
    


Ответы

Ответ 1



Если вы используете rest_framework, то можно использовать декоратор @list_route @list_route(method=['delete']) def remove_image(self, request): image_url = request.data['image_url'] item = self.model.objects.get(image_url=image_url) item.delete() return Response(status=status.HTTP_204_NO_CONTENT)

пятница, 27 декабря 2019 г.

Как сделать логин к сайту api без токена?

#ios #django #swift #django_rest_framework


Создал тестового юзера:
username: "david"
password: "framework"

let urlString = "http://gemcave.pythonanywhere.com/api/order"

            let email = "david"
            let password = "framework"

            let parameters = [
                "username": email,
                "password": password
            ]   

            Alamofire.request(.GET, urlString,parameters: parameters).responseJSON
{response in
                let json = JSON(response.data!)
                let swiftyJsonVar = json["truckType"].string
                print(swiftyJsonVar)
            }


Ошибка: return nil - аутентификация не проходит
Я не знаю в чем именно проблема в сервере или приложении

Бэкэнд: Django Rest Framework

JSON пример:

[
    {
        "id": 1,
        "textDep": "Paris",
        "textDest": "Deli",
        "truckType": "Грузовик1",
        "created_date": "2015-12-22T09:42:38Z",
        "author": 1
    },
    {
        "id": 4,
        "textDep": "Astana",
        "textDest": "Almata",
        "truckType": "Грузовик1",
        "created_date": "2015-12-22T09:44:13Z",
        "author": 1
    }
]



    


Ответы

Ответ 1



Могу предположить, что вы не активировали тестового юзера, попробуйте поднять django-admin, и оттуда уже проверить есть ли пользователь, активен ли он, есть ли у него соответствующие права, по части сервера всё сразу станет ясно.

вторник, 17 декабря 2019 г.

Django + Susy или Django + Bootstrap? Или другой вариант?

#django #bootstrap #angularjs #sass #django_rest_framework


Добрый день! Использую Django + Angular + Django REST.
Необходимо динамически размещать изображения разного размера плиткой, с возможностью
перемещения их посредством drag-n-drop. Для размещения использую сейчас bootstrap.js,
а для перетаскивания
Sortable.js + ng-sortable.js git:://github.com/rubaxa/Sortable.git
Но возникает проблема с картинками разного размера, возникает ситуация подобная этой



А нужно, чтобы было несколько колонок, и каждая картинка могла занимать одну или
несколько колонок, в зависимости от размера картинки. 
Что-то вроде этого (представим, что каждый блок - это картинка):



Пример для Susy с их сайта вроде ближе к тому, что должно быть:


Но до конца не уверен, подойдет ли мне этот фреймоворк для решения данной задачи?
Установил sudo gem install sass, sudo gem install susy, pip install libsass. Что
еще нужно установить и как использовать susy для django сайта?
Для Zend фреймворка нашел мануал: http://zencoder.ru/susy/brief-tutorial-susy-2/
Как мне сделать аналогичное для Django? При помощи susy или предложите, пожалуйста,
лучший вариант. По поводу susy не уверен.

Обновление:
Установил еще sudo apt-get install ruby-dev, sudo gem install compass.

Где подключать вот это, и в каком виде для Django?

@import "normalize";
@import "compass";
@import "susy";


Обновление 2:
Установил sudo pip install django-compass2, в settings.py добавил:

COMPASS_INPUT = BASE_DIR + 'media/sass'
COMPASS_OUTPUT = BASE_DIR + 'media/css'
COMPASS_STYLE = 'compact'
COMPASS_REQUIRES = (
    'ninesixty',  # 960.gs Grid System
)


и 'djcompass' в  INSTALLED_APPS.
Создал папки media/sass, media/css

При выполнении python manage.py compass
не находит модуль 'djcompass'.

Обновление 3:
пока закомментировал djcompass в INSTALLED_APPS.
Добавил в settings.py:

COMPRESS_PRECOMPILERS = (
    ('text/scss', 'sass --scss {infile} {outfile}'),
)


В шаблоне:

{% load compress %}
{% load staticfiles %}

{% compress css %}
    
{% endcompress %}


Ругается: 'compress' is not a valid tag library
Гугл говорит, что нужно сделать что-то вроде 

compass create [folder] -r rest-foundation --using foundation


Но compass мне не удалось настроить.
И не совсем понятно, для susy/compass/sass необходимо создавать отдельный проект
внутри проекта django? Пока не понимаю, как их всех вместе скрестить, чтобы работало:
Django + Django REST + Angular + SASS + Compass (и непонятно, можно ли обойтись без
него?) + Susy.

Обновление 4:
sass -v выдает Sass 3.4.18 (Selective Steve)

Установил pip install django-compressor
на основе этого http://django-compressor.readthedocs.org/en/latest/quickstart/
В settings добавил 

STATICFILES_FINDERS = (
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
    # other finders..
    'compressor.finders.CompressorFinder',
)


Добавил в INSTALLED_APPS "compressor" - после этого 500 ошибка.

При добавлении в TEMPLATES OPTIONS
django.templatetags.compress и django.contrib.staticfiles.templatetags.compress пишет,
что у таких модулей нет "compress" attribute/class

Обновление 5: нашел angular-gridster.js, который размещает вроде как нужно, + поддерживает
drag-n-drop http://manifestwebdesign.github.io/angular-gridster/?utm_source=angular-js.in&utm_medium=website&utm_campaign=content-curation
На этапе до sass/susy/compass:

bower install angular-gridster

 
    
    

Обновление 6


Подключил по мануалу на сайте разработчика, все работает. Единственная проблема -
как настроить angular-gripster, чтобы убрать z-слои и наложение изображений друг на друга?
    


Ответы

Ответ 1



Я бы подключил masonry. И в "бутстрапе", и в "susy", проблемы выравнивания блоков с разной высотой есть и будут. А "masonry" справится. Также рекомендую по возможности избавиться от "compass" - он перестал активно развиваться пару лет назад. Если нужна альтернатива - попробуйте bourbon.

вторник, 16 июля 2019 г.

AngularJS + Django REST, как удалить объект из списка?

Добрый день! Подскажите, пожалуйста, как добавить удаление объекта из списка? В контроллере в js добавил функцию, которая выполняет $http.delete('someurl' + id, {'id': id}); Если я правильно понимаю, delete метод работает только для url вида .../номер, например, /someurl/1? Без номер в конце ругается, что такой метод NOT ALLOWED.
При использовании $http.delete('someurl' + id, {'id': id}); судя по консоли выполняется DELETE http://someurl/1 и выдается ошибка 404 NOT FOUND. Страницы http://someurl/1 у меня нет и она мне как таковая не нужна, необходимо лишь каким-то образом удалить указанный объект. Добавление работает: $http.put('someurl', {'id': id}). Правда, в этом случае выдает ошибку 200 BAD REQUEST, но объект добавляется.
В views.py:
class MyAPI(mixins.UpdateModelMixin, generics.ListCreateAPIView): model = MyModel
permission_classes = [ permissions.IsAuthenticated ]
def get_serializer_class(self): if self.request.method in ['PUT', 'POST']: return MySerializerBasic return MySerializer
def get_queryset(self): return self.model.objects.filter(myfield=self.request.myfield).order_by('myorder')
def create(self, request, *args, **kwargs): data = {'myfield': request.myfield.id, 'myorder': 100} data.update(request.data) serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True) self.perform_create(serializer) headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def put(self, request, *args, **kwargs): for myorder, element in enumerate(request.data['mylist']): self.model.objects.filter( myfield=request.myfield, id=element).update( myorder=myorder ) print(request.data['mylist']) return Response(status=status.HTTP_200_OK)
Что и куда нужно добавить для удаления элемента?
Пытаюсь добавить
def delete(self, request, *args, **kwargs): id = request.data['id'] item = self.get_object(id) item.delete() print(request.data['id']) return Response(status=status.HTTP_200_OK)
Но ничего не происходит В urls есть
url(r'^someurl$', MyApi.as_view())
Обновление: Добавил в urls перед
url(r'^someurl$', MyApi.as_view())
строку
url(r'^someurl/(?P\d+)/', MyApi.as_view())
В контроллере для удаления вызываю
$http.delete('someurl/' + id + '/');
Выдает ошибку 404 NOT FOUND
Функция удаления сейчас выглядит вот так: def delete(self, request, pk, format=None): item = self.model.objects.get(pk=pk) item.delete() print(pk) return Response(status=status.HTTP_200_OK)
Возникли дополнительные вопросы: почему ошибка 404, что за параметр pk - у меня нет такого поля в модели (делал по примеру https://stackoverflow.com/questions/26445450/how-to-delete-an-object-using-django-rest-framework ) И как можно посмотреть вывод print(...)?


Ответ

Всего-то нужно было добавить один параметр в url шаблон в urls.py.
Итого:
В urls.py нужно добавить шаблон для страницы удаления, в конце должен быть .../номер/, иначе запрос DELETE не будет выполняться.
url(r'^someurl/(?P\d+)/', MyApi.as_view(), name='delete')
где name='delete' - это имя функции
Т.е. урл будет вида
...someurl/123/
В views.py добавить
def delete(self, request, pk, format=None): item = self.model.objects.get(pk=pk) item.delete() print(pk) return Response(status=status.HTTP_204_NO_CONTENT
В контроллере вызываю
$http.delete('someurl/' + id + '/');
При таком запросе удалится объект с номером = id
Всем спасибо за помощь!

среда, 10 июля 2019 г.

Django REST Framework - как заполнить шаблон данными без использования Django моделей?

Как заполнить шаблон без моделей? Использую Django REST, python 3.4, Angular JS
Предположим, есть 4 страницы в виде шаблонов: main.html, page1.html, page2.html, page3.html
На странице main отображается список, который получается из запроса к базе данных. При выборе какого-либо пункта необходимо осуществлять переход на страницу page1, и на ней отображать список, полученный также из результатов выполнения запроса к базе данных (база данных не Django), но уже с учетом выбранного на странице main пункта, т.е. передать несколько параметров в REST, заполнить шаблон page1.html результатами выполнения запроса и отобразить итоговую страницу. Далее при выборе пункта осуществлять аналогично переход на страницу page2, с нее на page3 и т.д.
Просто делать у пунктов ссылки вида /page1/type=sometype&id=1 на странице main, на page1 - /page2/type=secondtype&id=6 и т.д? Какого типа лучше использовать класс для обработки запросов - APIView? Пытаюсь использовать APIView, перехожу по ссылке вида /page1?id=1 Вот пример без type параметра, только с id
class SomeAPI(APIView): permission_classes = [permissions.AllowAny]
def get(self, request, *args, **kwargs): if (request.GET.get('id',None)): ... # result = получаю из базы данных набор записей на основе id return Response({'res':result}, template_name="page1.html")
в urls.py: url(r'^page1'), SomeAPI.as_view()
В шаблоне page1.html
... {{item}} ...
Записи возвращаются верно (т.е. с получением данных из БД проблем нет), но отображаются не в шаблоне page1.html, а на странице стандартного шаблона REST API в виде JSON-структуры
{ "res": [ { "item": "1" }, { "item": "2" }, { "item": "3" }, { "item": "4" } ] }
По сути мне нужно объединить функционал TemplateView + APIView. Можно было бы аналогично, как на странице main, из контроллера AngularJS запрашивать у APIView JSON результат методом GET, и заполнять template. Но для page1, page2, page3 нужно использовать id. Передать запросом GET его не проблема, и сформировать ответ в APIView тоже, но как передать в контроллер для страницы page1 этот id со страницы main, например? И как заполнить на основе JSON-ответа шаблон?


Ответ

Чтобы отправить словарь в шаблон, нужно использовать TemplateHTMLRenderer в параметре renderer_classes,а сам шаблон добавляется в параметр template_name
from rest_framework.renderers import TemplateHTMLRenderer from rest_framework.response import Response from rest_framework.views import APIView
class SomeAPI(APIView): renderer_classes = [TemplateHTMLRenderer] template_name = 'page1.html'
def get(self, request, *args, **kwargs): ... return Response({'res':result})
Документация по работе с шаблонами
Вообще лучше разделить бекенд и фронтенд, роутинг настроить в Angular, а REST использовать только для отправки данных.