From 545f380233b27882a9e8092f40393a1ec8d2040d Mon Sep 17 00:00:00 2001 From: tamer Date: Wed, 3 Jun 2020 23:51:21 +0300 Subject: [PATCH 01/12] create a post dfr --- kodilan/urls.py | 1 + posts/models.py | 8 ++++---- posts/serializers.py | 46 +++++++++++++++++++++++++++++++++++--------- 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/kodilan/urls.py b/kodilan/urls.py index 773ea9b..3595c20 100644 --- a/kodilan/urls.py +++ b/kodilan/urls.py @@ -24,6 +24,7 @@ path('tags/', views.TagsView.as_view()), path('companies/', views.CompaniesView.as_view()), path('posts/', views.PostsView.as_view()), + path('posts/add', views.CreatePostsView.as_view()), path('locations/', views.FindLocationAction.as_view()), path('', include(router.urls)), path('admin/', admin.site.urls), diff --git a/posts/models.py b/posts/models.py index 9d85941..9fd8153 100644 --- a/posts/models.py +++ b/posts/models.py @@ -12,8 +12,8 @@ class Meta: www = models.URLField() twitter = models.CharField(max_length=15) linkedin = models.URLField() - pub_date = models.DateTimeField() - created_at = models.DateTimeField() + pub_date = models.DateTimeField(null=True) + created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return self.name @@ -22,7 +22,7 @@ def __str__(self): class Tag(models.Model): name = models.CharField(max_length=200) slug = models.SlugField(null=True, blank=True) - created_at = models.DateTimeField() + created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return self.name @@ -60,7 +60,7 @@ class StatusEnum(models.IntegerChoices): renewal_code = models.CharField(max_length=200, null=True, blank=True) notes = models.TextField(null=True, blank=True) pub_date = models.DateTimeField(null=True) - created_at = models.DateTimeField() + created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return self.company.name + ' > ' + self.position diff --git a/posts/serializers.py b/posts/serializers.py index 0e7a9ec..2b3df80 100644 --- a/posts/serializers.py +++ b/posts/serializers.py @@ -1,38 +1,66 @@ from .models import Post, Company, Tag from rest_framework import serializers +from django.template.defaultfilters import slugify class CompanySerializer(serializers.HyperlinkedModelSerializer): + name = serializers.CharField(required=True, max_length=190) + slug = serializers.CharField(read_only=True) + logo = serializers.CharField(required=True, max_length=190) + www = serializers.URLField(required=True, max_length=190) + twitter = serializers.CharField(required=True, max_length=190) + linkedin = serializers.CharField(required=True, max_length=190) + class Meta: model = Company fields = ['name', 'slug', 'logo', 'www', 'twitter', 'linkedin'] class TagSerializer(serializers.ModelSerializer): + name = serializers.CharField(required=False, max_length=190) + slug = serializers.CharField(read_only=True) + class Meta: model = Tag fields = ['name', 'slug'] class CreatePostSerializer(serializers.ModelSerializer): + slug = serializers.CharField(read_only=True) + pub_date = serializers.CharField(read_only=True) + status = serializers.CharField(read_only=True) + position = serializers.CharField(required=True, max_length=190) description = serializers.CharField(required=True, min_length=4, max_length=100000) - position = serializers.CharField(required=False, allow_blank=True, max_length=190) - - apply_url = serializers.CharField(required=False, allow_blank=True, max_length=190) - apply_email = serializers.CharField(required=False, allow_blank=True, max_length=190) + apply_url = serializers.URLField(required=True, max_length=190) + apply_email = serializers.EmailField(required=True, max_length=190) + location = serializers.CharField(required=True, max_length=190) + type = serializers.ChoiceField(required=True, choices=Post.TypeEnum) - type = serializers.CharField(required=False, allow_blank=True, max_length=190) + company = CompanySerializer() - # todo diğer alanlar eklenecek + tags = TagSerializer(many=True) class Meta: model = Post - fields = ['slug', 'position', 'description', 'apply_url', 'apply_email', 'location', 'type', 'status', - 'is_featured', 'pub_date', 'post_url', 'company', 'tags'] + fields = ('slug', 'position', 'description', 'apply_url', 'apply_email', 'location', 'type', 'status', + 'is_featured', 'pub_date', 'post_url', 'company', 'tags') def create(self, validated_data): + + company_serializer = validated_data['company'] + company_slug = slugify(company_serializer['name']) + validated_data['company'] = Company.objects.create(slug=company_slug, **company_serializer) + + tags_serializer = validated_data['tags'] + del validated_data['tags'] + + slug = slugify("%s-%s" % ("company", validated_data['position'])) + post = Post.objects.create(status=0, slug=slug, **validated_data) + for item in tags_serializer: + tags_slug = slugify(item['name']) + tag = Tag.objects.create(slug=tags_slug, **item) + post.tags.add(tag) # todo mail gönderimi yapıalcak - post = Post.objects.create(status=0, **validated_data) return post From 0f2e36a2810edf0256cb955f2b9e260dbf2013a6 Mon Sep 17 00:00:00 2001 From: tamer Date: Fri, 5 Jun 2020 00:59:37 +0300 Subject: [PATCH 02/12] create post updated and fix --- kodilan/mail.py | 5 ++-- kodilan/settings.py | 12 ++++++++-- kodilan/templates/mail/activation.html | 2 +- kodilan/templates/mail/activation.txt | 2 +- posts/admin.py | 3 ++- posts/models.py | 2 +- posts/serializers.py | 33 ++++++++++++-------------- posts/views.py | 8 +++---- 8 files changed, 36 insertions(+), 31 deletions(-) diff --git a/kodilan/mail.py b/kodilan/mail.py index a2cd156..81ab864 100644 --- a/kodilan/mail.py +++ b/kodilan/mail.py @@ -20,7 +20,6 @@ def do_mail(email, title, theme, variables): pass -# todo: create post will update -def send_activation(email, first_name, last_name, token): +def send_activation(email, company, token): do_mail(email, "İlan Onay", "activation", - {'first_name': first_name, 'last_name': last_name, 'code': token}) + {'company': company, 'code': token}) diff --git a/kodilan/settings.py b/kodilan/settings.py index e914225..65efb22 100644 --- a/kodilan/settings.py +++ b/kodilan/settings.py @@ -38,7 +38,11 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + # 3rd party 'rest_framework', + 'django_filters', + # Local + 'kodilan' ] MIDDLEWARE = [ @@ -56,15 +60,18 @@ TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [os.path.join(BASE_DIR, 'templates')] - , + 'DIRS': [ + os.path.normpath(os.path.join(BASE_DIR, 'templates')), + ], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ + 'django.template.context_processors.media', 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', + 'django.template.context_processors.request', ], }, }, @@ -132,5 +139,6 @@ REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'kodilan.customs.StandardResultsSetPagination', + 'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'], 'PAGE_SIZE': 10 } diff --git a/kodilan/templates/mail/activation.html b/kodilan/templates/mail/activation.html index 874f42d..e0e7705 100644 --- a/kodilan/templates/mail/activation.html +++ b/kodilan/templates/mail/activation.html @@ -1,5 +1,5 @@
-

Merhaba {{ first_name }} {{ last_name }}

+

Merhaba {{ company }}

Kayıt işlemleriniz başarı ile gerçekleştirildi. Aşağıdaki üyelik onay linkine tıklayarak aktivasyon işleminizi gerçekleştirebilirsiniz.

https://kodilan.com/register/activation?code={{ code }}

diff --git a/kodilan/templates/mail/activation.txt b/kodilan/templates/mail/activation.txt index d3d6ea0..2a88aa7 100644 --- a/kodilan/templates/mail/activation.txt +++ b/kodilan/templates/mail/activation.txt @@ -1,4 +1,4 @@ -Merhaba {{ first_name }} {{ last_name }} +Merhaba {{ company }} Kayıt işlemleriniz başarı ile gerçekleştirildi. Aşağıdaki üyelik onay linkine tıklayarak aktivasyon işleminizi gerçekleştirebilirsiniz. https://kodilan.com/register/activation?code={{ code }} diff --git a/posts/admin.py b/posts/admin.py index 7416326..3178952 100644 --- a/posts/admin.py +++ b/posts/admin.py @@ -4,7 +4,8 @@ class PostAdmin(admin.ModelAdmin): - list_display = ('id', 'position', 'detail', 'location', 'status', 'pub_date') + list_display = ('id', 'slug', 'position', 'detail', 'location', 'status', 'pub_date') + list_filter = ('status', 'position') admin.site.register(Company) diff --git a/posts/models.py b/posts/models.py index 9fd8153..2d8bc0f 100644 --- a/posts/models.py +++ b/posts/models.py @@ -20,7 +20,7 @@ def __str__(self): class Tag(models.Model): - name = models.CharField(max_length=200) + name = models.CharField(max_length=200, unique=True) slug = models.SlugField(null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) diff --git a/posts/serializers.py b/posts/serializers.py index 2b3df80..6723e6b 100644 --- a/posts/serializers.py +++ b/posts/serializers.py @@ -1,6 +1,8 @@ from .models import Post, Company, Tag from rest_framework import serializers from django.template.defaultfilters import slugify +from kodilan.mail import send_activation +import secrets class CompanySerializer(serializers.HyperlinkedModelSerializer): @@ -25,7 +27,7 @@ class Meta: fields = ['name', 'slug'] -class CreatePostSerializer(serializers.ModelSerializer): +class PostSerializer(serializers.ModelSerializer): slug = serializers.CharField(read_only=True) pub_date = serializers.CharField(read_only=True) status = serializers.CharField(read_only=True) @@ -37,8 +39,7 @@ class CreatePostSerializer(serializers.ModelSerializer): type = serializers.ChoiceField(required=True, choices=Post.TypeEnum) company = CompanySerializer() - - tags = TagSerializer(many=True) + tags = TagSerializer(many=True, required=False) class Meta: model = Post @@ -51,24 +52,20 @@ def create(self, validated_data): company_slug = slugify(company_serializer['name']) validated_data['company'] = Company.objects.create(slug=company_slug, **company_serializer) - tags_serializer = validated_data['tags'] - del validated_data['tags'] + tags_serializer = [] + if 'tags' in validated_data: + tags_serializer = validated_data['tags'] + del validated_data['tags'] + + slug = slugify("%s-%s" % (company_slug, validated_data['position'])) + token = secrets.token_urlsafe(24) + post = Post.objects.create(status=0, slug=slug, activation_code=token, **validated_data) - slug = slugify("%s-%s" % ("company", validated_data['position'])) - post = Post.objects.create(status=0, slug=slug, **validated_data) for item in tags_serializer: tags_slug = slugify(item['name']) - tag = Tag.objects.create(slug=tags_slug, **item) + tag = Tag.objects.get_or_create(slug=tags_slug, **item) post.tags.add(tag) - # todo mail gönderimi yapıalcak - return post - -class PostSerializer(serializers.ModelSerializer): - tags = TagSerializer(many=True) - company = CompanySerializer() + send_activation(validated_data['apply_email'], company_serializer['name'], token) - class Meta: - model = Post - fields = ['slug', 'position', 'description', 'apply_url', 'apply_email', 'location', 'type', 'status', - 'is_featured', 'pub_date', 'post_url', 'company', 'tags'] + return post diff --git a/posts/views.py b/posts/views.py index a9eb004..4124f6b 100644 --- a/posts/views.py +++ b/posts/views.py @@ -1,5 +1,5 @@ from .models import Post, Company, Tag -from .serializers import PostSerializer, CompanySerializer, TagSerializer, CreatePostSerializer +from .serializers import PostSerializer, CompanySerializer, TagSerializer from rest_framework.response import Response from rest_framework.views import APIView from rest_framework.generics import ListAPIView, CreateAPIView @@ -11,7 +11,7 @@ class CompaniesView(ListAPIView): queryset = Company.objects.all() serializer_class = CompanySerializer - filter_backends = [filters.SearchFilter, filters.OrderingFilter] + filter_backends = [filters.SearchFilter, filters.OrderingFilter, ] search_fields = ['name'] ordering_fields = ['id', 'name', 'created_at'] @@ -21,14 +21,14 @@ class PostsView(ListAPIView): serializer_class = PostSerializer filter_backends = [StatusFilterBackend, PeriodFilterBackend, DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter] - + filterset_fields = ['tags', 'type', 'position', 'is_featured'] search_fields = ['is_featured', 'location', 'type', 'position', 'tags'] ordering_fields = ['pub_date', 'created_at'] class CreatePostsView(CreateAPIView): queryset = Post.objects.all() - serializer_class = CreatePostSerializer + serializer_class = PostSerializer class TagsView(ListAPIView): From 2e12d2dc992fa669ea880ef6eb611c4e3acb3dec Mon Sep 17 00:00:00 2001 From: tamer Date: Fri, 5 Jun 2020 01:09:43 +0300 Subject: [PATCH 03/12] tags unique name added create or get --- posts/serializers.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/posts/serializers.py b/posts/serializers.py index 6723e6b..a7dc0b2 100644 --- a/posts/serializers.py +++ b/posts/serializers.py @@ -2,6 +2,7 @@ from rest_framework import serializers from django.template.defaultfilters import slugify from kodilan.mail import send_activation +from django.db import IntegrityError import secrets @@ -63,7 +64,11 @@ def create(self, validated_data): for item in tags_serializer: tags_slug = slugify(item['name']) - tag = Tag.objects.get_or_create(slug=tags_slug, **item) + try: + tag = Tag.objects.create(slug=tags_slug, **item) + except IntegrityError: + tag = Tag.objects.get(name=item['name']) + post.tags.add(tag) send_activation(validated_data['apply_email'], company_serializer['name'], token) From 2d1e9bc3f55d87e988c28d8f1bd2d074c451a03b Mon Sep 17 00:00:00 2001 From: tamer Date: Fri, 5 Jun 2020 01:41:48 +0300 Subject: [PATCH 04/12] add redoc --- kodilan/templates/redoc.html | 21 +++++++++++++++++++++ kodilan/urls.py | 13 +++++++++++++ posts/models.py | 4 ++-- posts/serializers.py | 4 ++-- requirements.txt | 4 +++- 5 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 kodilan/templates/redoc.html diff --git a/kodilan/templates/redoc.html b/kodilan/templates/redoc.html new file mode 100644 index 0000000..c02bbf9 --- /dev/null +++ b/kodilan/templates/redoc.html @@ -0,0 +1,21 @@ + + + + ReDoc + + + + + + + + + + + + \ No newline at end of file diff --git a/kodilan/urls.py b/kodilan/urls.py index 3595c20..e6ddc02 100644 --- a/kodilan/urls.py +++ b/kodilan/urls.py @@ -17,6 +17,9 @@ from django.urls import include, path from rest_framework import routers from posts import views +from django.views.generic import TemplateView +from rest_framework.schemas import get_schema_view +from rest_framework.renderers import JSONOpenAPIRenderer router = routers.DefaultRouter() @@ -27,5 +30,15 @@ path('posts/add', views.CreatePostsView.as_view()), path('locations/', views.FindLocationAction.as_view()), path('', include(router.urls)), + path('schema', get_schema_view( + title="Kodilan", + description="API for all things …", + version="0.0.0", + renderer_classes=[JSONOpenAPIRenderer] + ), name='docs-schema'), + path('redoc/', TemplateView.as_view( + template_name='redoc.html', + extra_context={'schema_url': 'docs-schema'} + ), name='docs-redoc'), path('admin/', admin.site.urls), ] diff --git a/posts/models.py b/posts/models.py index 2d8bc0f..ad22643 100644 --- a/posts/models.py +++ b/posts/models.py @@ -10,8 +10,8 @@ class Meta: email = models.EmailField() logo = models.ImageField(upload_to='uploads/') www = models.URLField() - twitter = models.CharField(max_length=15) - linkedin = models.URLField() + twitter = models.CharField(null=True, max_length=15) + linkedin = models.URLField(null=True,) pub_date = models.DateTimeField(null=True) created_at = models.DateTimeField(auto_now_add=True) diff --git a/posts/serializers.py b/posts/serializers.py index a7dc0b2..03f5984 100644 --- a/posts/serializers.py +++ b/posts/serializers.py @@ -11,8 +11,8 @@ class CompanySerializer(serializers.HyperlinkedModelSerializer): slug = serializers.CharField(read_only=True) logo = serializers.CharField(required=True, max_length=190) www = serializers.URLField(required=True, max_length=190) - twitter = serializers.CharField(required=True, max_length=190) - linkedin = serializers.CharField(required=True, max_length=190) + twitter = serializers.CharField(required=False, max_length=190) + linkedin = serializers.CharField(required=False, max_length=190) class Meta: model = Company diff --git a/requirements.txt b/requirements.txt index 2883bbf..8b3cb4f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,4 +10,6 @@ django-filter django-filters django-redirect-to-non-www django-rest-framework -djangorestframework-simplejwt \ No newline at end of file +djangorestframework-simplejwt +pyyaml +uritemplate \ No newline at end of file From 928dacf22f6abae91327868ae3de6284fd2fb0c2 Mon Sep 17 00:00:00 2001 From: tamer Date: Fri, 5 Jun 2020 09:26:23 +0300 Subject: [PATCH 05/12] this file is generating exp: 'python3 manage.py generateschema > openapi-schema.yml' --- openapi-schema.yml | 537 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 537 insertions(+) create mode 100644 openapi-schema.yml diff --git a/openapi-schema.yml b/openapi-schema.yml new file mode 100644 index 0000000..ed1f93f --- /dev/null +++ b/openapi-schema.yml @@ -0,0 +1,537 @@ +openapi: 3.0.2 +info: + title: '' + version: '' +paths: + /tags/: + get: + operationId: listTags + description: '' + parameters: + - name: page + required: false + in: query + description: A page number within the paginated result set. + schema: + type: integer + - name: page_size + required: false + in: query + description: Number of results to return per page. + schema: + type: integer + - name: search + required: false + in: query + description: A search term. + schema: + type: string + - name: ordering + required: false + in: query + description: Which field to use when ordering the results. + schema: + type: string + responses: + '200': + content: + application/json: + schema: + type: object + properties: + count: + type: integer + example: 123 + next: + type: string + nullable: true + previous: + type: string + nullable: true + results: + type: array + items: + properties: + name: + type: string + maxLength: 190 + slug: + type: string + readOnly: true + description: '' + /companies/: + get: + operationId: listCompanys + description: '' + parameters: + - name: page + required: false + in: query + description: A page number within the paginated result set. + schema: + type: integer + - name: page_size + required: false + in: query + description: Number of results to return per page. + schema: + type: integer + - name: search + required: false + in: query + description: A search term. + schema: + type: string + - name: ordering + required: false + in: query + description: Which field to use when ordering the results. + schema: + type: string + responses: + '200': + content: + application/json: + schema: + type: object + properties: + count: + type: integer + example: 123 + next: + type: string + nullable: true + previous: + type: string + nullable: true + results: + type: array + items: + properties: + name: + type: string + maxLength: 190 + slug: + type: string + readOnly: true + logo: + type: string + maxLength: 190 + www: + type: string + format: uri + maxLength: 190 + pattern: "^(?:[a-z0-9\\.\\-\\+]*)://(?:[^\\s:@/]+(?::[^\\\ + s:@/]*)?@)?(?:(?:25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(?:\\\ + .(?:25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}|\\[[0-9a-f:\\\ + .]+\\]|([a-z\xA1-\uFFFF0-9](?:[a-z\xA1-\uFFFF0-9-]{0,61}[a-z\xA1\ + -\uFFFF0-9])?(?:\\.(?!-)[a-z\xA1-\uFFFF0-9-]{1,63}(? Date: Sat, 6 Jun 2020 21:19:43 +0300 Subject: [PATCH 06/12] #9 issue is solved --- posts/admin.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/posts/admin.py b/posts/admin.py index 3178952..2b24d9e 100644 --- a/posts/admin.py +++ b/posts/admin.py @@ -1,11 +1,31 @@ from django.contrib import admin from .models import Company, Post, Tag +from django.utils.html import format_html +from django.urls import reverse + + +def make_published(modeladmin, request, queryset): + queryset.update(status=1) + + +def make_unpublished(modeladmin, request, queryset): + queryset.update(status=2) + + +def make_disapproved(modeladmin, request, queryset): + queryset.update(status=0) + + +make_unpublished.short_description = "Mark posts stories as unpublished" +make_published.short_description = "Mark posts stories as published" +make_disapproved.short_description = "Mark posts stories as disapproved" class PostAdmin(admin.ModelAdmin): list_display = ('id', 'slug', 'position', 'detail', 'location', 'status', 'pub_date') list_filter = ('status', 'position') + actions = [make_published, make_unpublished, make_disapproved] admin.site.register(Company) From 4053e7c6b567ae1f289b56c408d9b408b2dd2282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tamer=20A=C4=9Fao=C4=9Flu?= Date: Sat, 6 Jun 2020 22:08:28 +0300 Subject: [PATCH 07/12] added material admin and added updating status in pub_date from posts --- kodilan/settings.py | 3 ++- kodilan/urls.py | 4 ++++ posts/admin.py | 7 ++++--- requirements.txt | 3 ++- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/kodilan/settings.py b/kodilan/settings.py index 65efb22..b45a9eb 100644 --- a/kodilan/settings.py +++ b/kodilan/settings.py @@ -31,8 +31,9 @@ # Application definition INSTALLED_APPS = [ + 'material.admin', + 'material.admin.default', 'posts.apps.PostsConfig', - 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', diff --git a/kodilan/urls.py b/kodilan/urls.py index e6ddc02..bf6992a 100644 --- a/kodilan/urls.py +++ b/kodilan/urls.py @@ -42,3 +42,7 @@ ), name='docs-redoc'), path('admin/', admin.site.urls), ] + +admin.site.site_header = "Kodilan Admin" +admin.site.site_title = "Kodilan Api" +admin.site.index_title = "Welcome to Kodilan Researcher Admin" \ No newline at end of file diff --git a/posts/admin.py b/posts/admin.py index 2b24d9e..a00822d 100644 --- a/posts/admin.py +++ b/posts/admin.py @@ -3,18 +3,19 @@ from .models import Company, Post, Tag from django.utils.html import format_html from django.urls import reverse +from datetime import datetime def make_published(modeladmin, request, queryset): - queryset.update(status=1) + queryset.update(status=1, pub_date=datetime.now()) def make_unpublished(modeladmin, request, queryset): - queryset.update(status=2) + queryset.update(status=2, pub_date=None) def make_disapproved(modeladmin, request, queryset): - queryset.update(status=0) + queryset.update(status=0, pub_date=None) make_unpublished.short_description = "Mark posts stories as unpublished" diff --git a/requirements.txt b/requirements.txt index 8b3cb4f..de07c20 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,4 +12,5 @@ django-redirect-to-non-www django-rest-framework djangorestframework-simplejwt pyyaml -uritemplate \ No newline at end of file +uritemplate +django-material-admin \ No newline at end of file From 2dbca8baa14c0e91d31b513bcc1bbbaf4499c871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tamer=20A=C4=9Fao=C4=9Flu?= Date: Sun, 7 Jun 2020 08:40:43 +0300 Subject: [PATCH 08/12] activation done and added unique fields --- kodilan/urls.py | 1 + posts/admin.py | 19 ++++++++++++++++--- posts/models.py | 4 ++-- posts/serializers.py | 28 +++++++++++++++++++++++++--- posts/views.py | 10 ++++++++-- 5 files changed, 52 insertions(+), 10 deletions(-) diff --git a/kodilan/urls.py b/kodilan/urls.py index bf6992a..3ad2c59 100644 --- a/kodilan/urls.py +++ b/kodilan/urls.py @@ -28,6 +28,7 @@ path('companies/', views.CompaniesView.as_view()), path('posts/', views.PostsView.as_view()), path('posts/add', views.CreatePostsView.as_view()), + path('posts/activation/', views.ActivatePostView.as_view()), path('locations/', views.FindLocationAction.as_view()), path('', include(router.urls)), path('schema', get_schema_view( diff --git a/posts/admin.py b/posts/admin.py index a00822d..6669989 100644 --- a/posts/admin.py +++ b/posts/admin.py @@ -1,9 +1,22 @@ from django.contrib import admin from .models import Company, Post, Tag -from django.utils.html import format_html -from django.urls import reverse from datetime import datetime +from django.contrib.admin import SimpleListFilter + + +class ActivationFilter(SimpleListFilter): + title = 'activation' + parameter_name = 'activation_code' + + def lookups(self, request, model_admin): + return [('activated', 'Activated'), ('hold', 'On Hold')] + + def queryset(self, request, queryset): + if self.value() == 'activated': + return queryset.filter(activation_code__isnull=True) + if self.value() == 'hold': + return queryset.filter(activation_code__isnull=False) def make_published(modeladmin, request, queryset): @@ -25,7 +38,7 @@ def make_disapproved(modeladmin, request, queryset): class PostAdmin(admin.ModelAdmin): list_display = ('id', 'slug', 'position', 'detail', 'location', 'status', 'pub_date') - list_filter = ('status', 'position') + list_filter = (ActivationFilter, 'status', 'position') actions = [make_published, make_unpublished, make_disapproved] diff --git a/posts/models.py b/posts/models.py index ad22643..839257c 100644 --- a/posts/models.py +++ b/posts/models.py @@ -5,9 +5,9 @@ class Company(models.Model): class Meta: verbose_name_plural = "companies" - name = models.CharField(max_length=200) + name = models.CharField(max_length=200, unique=True) slug = models.SlugField(null=True) - email = models.EmailField() + email = models.EmailField(unique=True) logo = models.ImageField(upload_to='uploads/') www = models.URLField() twitter = models.CharField(null=True, max_length=15) diff --git a/posts/serializers.py b/posts/serializers.py index 03f5984..470db71 100644 --- a/posts/serializers.py +++ b/posts/serializers.py @@ -4,11 +4,13 @@ from kodilan.mail import send_activation from django.db import IntegrityError import secrets +from django.db.models import Q class CompanySerializer(serializers.HyperlinkedModelSerializer): name = serializers.CharField(required=True, max_length=190) slug = serializers.CharField(read_only=True) + email = serializers.EmailField(required=True, max_length=190) logo = serializers.CharField(required=True, max_length=190) www = serializers.URLField(required=True, max_length=190) twitter = serializers.CharField(required=False, max_length=190) @@ -16,7 +18,7 @@ class CompanySerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Company - fields = ['name', 'slug', 'logo', 'www', 'twitter', 'linkedin'] + fields = ['name', 'slug', 'email', 'logo', 'www', 'twitter', 'linkedin'] class TagSerializer(serializers.ModelSerializer): @@ -30,6 +32,7 @@ class Meta: class PostSerializer(serializers.ModelSerializer): slug = serializers.CharField(read_only=True) + is_featured = serializers.BooleanField(read_only=True) pub_date = serializers.CharField(read_only=True) status = serializers.CharField(read_only=True) position = serializers.CharField(required=True, max_length=190) @@ -51,7 +54,11 @@ def create(self, validated_data): company_serializer = validated_data['company'] company_slug = slugify(company_serializer['name']) - validated_data['company'] = Company.objects.create(slug=company_slug, **company_serializer) + try: + validated_data['company'] = Company.objects.create(slug=company_slug, **company_serializer) + except IntegrityError: + validated_data['company'] = Company.objects.filter( + Q(name=company_serializer['name']) | Q(email=company_serializer['email'])).first() tags_serializer = [] if 'tags' in validated_data: @@ -60,7 +67,7 @@ def create(self, validated_data): slug = slugify("%s-%s" % (company_slug, validated_data['position'])) token = secrets.token_urlsafe(24) - post = Post.objects.create(status=0, slug=slug, activation_code=token, **validated_data) + post = Post.objects.create(status=0, is_featured=False, slug=slug, activation_code=token, **validated_data) for item in tags_serializer: tags_slug = slugify(item['name']) @@ -74,3 +81,18 @@ def create(self, validated_data): send_activation(validated_data['apply_email'], company_serializer['name'], token) return post + + +class ActivatePostSerializer(serializers.ModelSerializer): + status = serializers.SerializerMethodField() + + class Meta: + model = Post + fields = ('id', 'status') + + def get_status(self, obj): + obj.activation_code = None + obj.save() + # send_activation_status(obj.apply_email, obj.company.name) + # if you want you may send activation status success + return True diff --git a/posts/views.py b/posts/views.py index 4124f6b..d0e10c7 100644 --- a/posts/views.py +++ b/posts/views.py @@ -1,8 +1,8 @@ from .models import Post, Company, Tag -from .serializers import PostSerializer, CompanySerializer, TagSerializer +from .serializers import PostSerializer, CompanySerializer, TagSerializer, ActivatePostSerializer from rest_framework.response import Response from rest_framework.views import APIView -from rest_framework.generics import ListAPIView, CreateAPIView +from rest_framework.generics import ListAPIView, CreateAPIView, RetrieveAPIView from rest_framework import filters from django_filters.rest_framework import DjangoFilterBackend from .filters import PeriodFilterBackend, StatusFilterBackend @@ -31,6 +31,12 @@ class CreatePostsView(CreateAPIView): serializer_class = PostSerializer +class ActivatePostView(RetrieveAPIView): + queryset = Post.objects.all() + serializer_class = ActivatePostSerializer + lookup_field = "activation_code" + + class TagsView(ListAPIView): queryset = Tag.objects.all() serializer_class = TagSerializer From 87a639d705ccf0070d7cf993313cbf11dc13e9b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tamer=20A=C4=9Fao=C4=9Flu?= Date: Mon, 8 Jun 2020 00:57:39 +0300 Subject: [PATCH 09/12] =?UTF-8?q?location=20ve=20di=C4=9Fer=20endpointler?= =?UTF-8?q?=20g=C3=BCncellendi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kodilan/settings.py | 16 ++++++++++++++-- kodilan/urls.py | 11 ++++++----- posts/admin.py | 15 +++++++++++++-- posts/models.py | 2 +- posts/serializers.py | 8 ++++++++ posts/views.py | 26 ++++++++++++++------------ 6 files changed, 56 insertions(+), 22 deletions(-) diff --git a/kodilan/settings.py b/kodilan/settings.py index b45a9eb..b2d291d 100644 --- a/kodilan/settings.py +++ b/kodilan/settings.py @@ -33,7 +33,6 @@ INSTALLED_APPS = [ 'material.admin', 'material.admin.default', - 'posts.apps.PostsConfig', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', @@ -42,11 +41,14 @@ # 3rd party 'rest_framework', 'django_filters', + 'corsheaders', # Local - 'kodilan' + 'kodilan', + 'posts.apps.PostsConfig' ] MIDDLEWARE = [ + 'corsheaders.middleware.CorsMiddleware', 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', @@ -58,6 +60,16 @@ ROOT_URLCONF = 'kodilan.urls' + +CORS_ORIGIN_WHITELIST = ( + 'https://kodilan.com', + 'http://kodilan.com', + 'http://localhost:3000', + 'http://localhost:8000', + 'http://localhost:8081', + 'http://localhost:8080', +) + TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', diff --git a/kodilan/urls.py b/kodilan/urls.py index 3ad2c59..a1adf50 100644 --- a/kodilan/urls.py +++ b/kodilan/urls.py @@ -24,12 +24,13 @@ router = routers.DefaultRouter() urlpatterns = [ - path('tags/', views.TagsView.as_view()), - path('companies/', views.CompaniesView.as_view()), - path('posts/', views.PostsView.as_view()), + path('tags', views.TagsView.as_view()), + path('companies', views.CompaniesView.as_view()), + path('posts', views.PostsView.as_view()), + path('posts/slug/', views.PostView.as_view()), path('posts/add', views.CreatePostsView.as_view()), path('posts/activation/', views.ActivatePostView.as_view()), - path('locations/', views.FindLocationAction.as_view()), + path('locations', views.FindLocationAction.as_view()), path('', include(router.urls)), path('schema', get_schema_view( title="Kodilan", @@ -37,7 +38,7 @@ version="0.0.0", renderer_classes=[JSONOpenAPIRenderer] ), name='docs-schema'), - path('redoc/', TemplateView.as_view( + path('redoc', TemplateView.as_view( template_name='redoc.html', extra_context={'schema_url': 'docs-schema'} ), name='docs-redoc'), diff --git a/posts/admin.py b/posts/admin.py index 6669989..23a4c05 100644 --- a/posts/admin.py +++ b/posts/admin.py @@ -31,15 +31,26 @@ def make_disapproved(modeladmin, request, queryset): queryset.update(status=0, pub_date=None) +def make_featured(modeladmin, request, queryset): + queryset.update(is_featured=True) + + +def make_not_featured(modeladmin, request, queryset): + queryset.update(is_featured=False) + + make_unpublished.short_description = "Mark posts stories as unpublished" make_published.short_description = "Mark posts stories as published" make_disapproved.short_description = "Mark posts stories as disapproved" +make_featured.short_description = "Mark posts stories as featured" +make_not_featured.short_description = "Mark posts stories as featured" + class PostAdmin(admin.ModelAdmin): - list_display = ('id', 'slug', 'position', 'detail', 'location', 'status', 'pub_date') + list_display = ('id', 'slug', 'position', 'is_featured', 'detail', 'location', 'status', 'pub_date') list_filter = (ActivationFilter, 'status', 'position') - actions = [make_published, make_unpublished, make_disapproved] + actions = [make_published, make_unpublished, make_disapproved, make_featured, make_not_featured] admin.site.register(Company) diff --git a/posts/models.py b/posts/models.py index 839257c..86705b5 100644 --- a/posts/models.py +++ b/posts/models.py @@ -47,7 +47,7 @@ class StatusEnum(models.IntegerChoices): company = models.ForeignKey(Company, on_delete=models.CASCADE) tags = models.ManyToManyField(Tag) - slug = models.SlugField(null=True) + slug = models.SlugField(null=True, unique=True) position = models.CharField(max_length=200) description = models.TextField() apply_url = models.URLField(null=True, blank=True) diff --git a/posts/serializers.py b/posts/serializers.py index 470db71..54e7877 100644 --- a/posts/serializers.py +++ b/posts/serializers.py @@ -83,6 +83,14 @@ def create(self, validated_data): return post +class LocationSerializer(serializers.ModelSerializer): + location = serializers.CharField(required=True, max_length=190) + + class Meta: + model = Post + fields = ('location',) + + class ActivatePostSerializer(serializers.ModelSerializer): status = serializers.SerializerMethodField() diff --git a/posts/views.py b/posts/views.py index d0e10c7..0b3bdcc 100644 --- a/posts/views.py +++ b/posts/views.py @@ -1,7 +1,5 @@ from .models import Post, Company, Tag -from .serializers import PostSerializer, CompanySerializer, TagSerializer, ActivatePostSerializer -from rest_framework.response import Response -from rest_framework.views import APIView +from .serializers import PostSerializer, CompanySerializer, TagSerializer, ActivatePostSerializer, LocationSerializer from rest_framework.generics import ListAPIView, CreateAPIView, RetrieveAPIView from rest_framework import filters from django_filters.rest_framework import DjangoFilterBackend @@ -21,11 +19,18 @@ class PostsView(ListAPIView): serializer_class = PostSerializer filter_backends = [StatusFilterBackend, PeriodFilterBackend, DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter] - filterset_fields = ['tags', 'type', 'position', 'is_featured'] - search_fields = ['is_featured', 'location', 'type', 'position', 'tags'] + filterset_fields = ['tags', 'type', 'position', 'location', 'is_featured'] + search_fields = ['position', 'description'] ordering_fields = ['pub_date', 'created_at'] +class PostView(RetrieveAPIView): + queryset = Post.objects.all() + serializer_class = PostSerializer + lookup_field = "slug" + filter_backends = [StatusFilterBackend] + + class CreatePostsView(CreateAPIView): queryset = Post.objects.all() serializer_class = PostSerializer @@ -44,10 +49,7 @@ class TagsView(ListAPIView): search_fields = ['name'] -class FindLocationAction(APIView): - def get(self, request): - locations = Post.objects.all().distinct('location') - data = {} - for location in locations: - data[location.id] = location.location - return Response(data) +class FindLocationAction(ListAPIView): + queryset = Post.objects.all().values('location').distinct() + filter_backends = [StatusFilterBackend] + serializer_class = LocationSerializer From 804958a33e77fd53206283849f14f59299747409 Mon Sep 17 00:00:00 2001 From: tamer Date: Sat, 13 Jun 2020 00:01:21 +0300 Subject: [PATCH 10/12] get_post_url has changed to SerializerMethodField and added build_absolute_uri --- posts/migrations/0001_initial.py | 28 ------------ posts/migrations/0002_auto_20200601_1846.py | 30 ------------- posts/migrations/0003_auto_20200601_1853.py | 23 ---------- posts/migrations/0004_post.py | 36 ---------------- posts/migrations/0005_auto_20200601_1909.py | 43 ------------------ posts/migrations/0006_auto_20200601_1931.py | 48 --------------------- posts/migrations/0007_auto_20200601_1934.py | 18 -------- posts/models.py | 5 +-- posts/serializers.py | 6 +++ 9 files changed, 7 insertions(+), 230 deletions(-) delete mode 100644 posts/migrations/0001_initial.py delete mode 100644 posts/migrations/0002_auto_20200601_1846.py delete mode 100644 posts/migrations/0003_auto_20200601_1853.py delete mode 100644 posts/migrations/0004_post.py delete mode 100644 posts/migrations/0005_auto_20200601_1909.py delete mode 100644 posts/migrations/0006_auto_20200601_1931.py delete mode 100644 posts/migrations/0007_auto_20200601_1934.py diff --git a/posts/migrations/0001_initial.py b/posts/migrations/0001_initial.py deleted file mode 100644 index 1d93da1..0000000 --- a/posts/migrations/0001_initial.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 3.0.6 on 2020-06-01 18:09 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ] - - operations = [ - migrations.CreateModel( - name='Company', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=200)), - ('email', models.EmailField(max_length=254)), - ('logo', models.ImageField(upload_to='')), - ('www', models.URLField()), - ('twitter', models.CharField(max_length=15)), - ('linkedin', models.URLField()), - ('pub_date', models.DateTimeField()), - ('created_at', models.DateTimeField()), - ], - ), - ] diff --git a/posts/migrations/0002_auto_20200601_1846.py b/posts/migrations/0002_auto_20200601_1846.py deleted file mode 100644 index 7452793..0000000 --- a/posts/migrations/0002_auto_20200601_1846.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 3.0.6 on 2020-06-01 18:46 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('posts', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='Tag', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=200)), - ('created_at', models.DateTimeField()), - ], - ), - migrations.AlterModelOptions( - name='company', - options={'verbose_name_plural': 'companies'}, - ), - migrations.AlterField( - model_name='company', - name='logo', - field=models.ImageField(upload_to='uploads/'), - ), - ] diff --git a/posts/migrations/0003_auto_20200601_1853.py b/posts/migrations/0003_auto_20200601_1853.py deleted file mode 100644 index 207cbe8..0000000 --- a/posts/migrations/0003_auto_20200601_1853.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.0.6 on 2020-06-01 18:53 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('posts', '0002_auto_20200601_1846'), - ] - - operations = [ - migrations.AddField( - model_name='company', - name='slug', - field=models.SlugField(null=True), - ), - migrations.AddField( - model_name='tag', - name='slug', - field=models.SlugField(null=True), - ), - ] diff --git a/posts/migrations/0004_post.py b/posts/migrations/0004_post.py deleted file mode 100644 index f15d7ef..0000000 --- a/posts/migrations/0004_post.py +++ /dev/null @@ -1,36 +0,0 @@ -# Generated by Django 3.0.6 on 2020-06-01 19:06 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('posts', '0003_auto_20200601_1853'), - ] - - operations = [ - migrations.CreateModel( - name='Post', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('slug', models.SlugField(null=True)), - ('position', models.CharField(max_length=200)), - ('description', models.TextField()), - ('apply_url', models.URLField()), - ('apply_email', models.EmailField(max_length=254)), - ('location', models.CharField(max_length=200)), - ('type', models.SmallIntegerField()), - ('status', models.SmallIntegerField()), - ('is_featured', models.BooleanField()), - ('activation_code', models.CharField(max_length=200)), - ('renewal_code', models.CharField(max_length=200)), - ('notes', models.TextField()), - ('pub_date', models.DateTimeField()), - ('created_at', models.DateTimeField()), - ('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='posts.Company')), - ('tags', models.ManyToManyField(to='posts.Tag')), - ], - ), - ] diff --git a/posts/migrations/0005_auto_20200601_1909.py b/posts/migrations/0005_auto_20200601_1909.py deleted file mode 100644 index 82dd4dc..0000000 --- a/posts/migrations/0005_auto_20200601_1909.py +++ /dev/null @@ -1,43 +0,0 @@ -# Generated by Django 3.0.6 on 2020-06-01 19:09 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('posts', '0004_post'), - ] - - operations = [ - migrations.AlterField( - model_name='post', - name='activation_code', - field=models.CharField(max_length=200, null=True), - ), - migrations.AlterField( - model_name='post', - name='apply_email', - field=models.EmailField(max_length=254, null=True), - ), - migrations.AlterField( - model_name='post', - name='apply_url', - field=models.URLField(null=True), - ), - migrations.AlterField( - model_name='post', - name='notes', - field=models.TextField(null=True), - ), - migrations.AlterField( - model_name='post', - name='pub_date', - field=models.DateTimeField(null=True), - ), - migrations.AlterField( - model_name='post', - name='renewal_code', - field=models.CharField(max_length=200, null=True), - ), - ] diff --git a/posts/migrations/0006_auto_20200601_1931.py b/posts/migrations/0006_auto_20200601_1931.py deleted file mode 100644 index 8a4844a..0000000 --- a/posts/migrations/0006_auto_20200601_1931.py +++ /dev/null @@ -1,48 +0,0 @@ -# Generated by Django 3.0.6 on 2020-06-01 19:31 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('posts', '0005_auto_20200601_1909'), - ] - - operations = [ - migrations.AlterField( - model_name='post', - name='activation_code', - field=models.CharField(blank=True, max_length=200, null=True), - ), - migrations.AlterField( - model_name='post', - name='apply_email', - field=models.EmailField(blank=True, max_length=254, null=True), - ), - migrations.AlterField( - model_name='post', - name='apply_url', - field=models.URLField(blank=True, null=True), - ), - migrations.AlterField( - model_name='post', - name='notes', - field=models.TextField(blank=True, null=True), - ), - migrations.AlterField( - model_name='post', - name='renewal_code', - field=models.CharField(blank=True, max_length=200, null=True), - ), - migrations.AlterField( - model_name='post', - name='status', - field=models.PositiveSmallIntegerField(choices=[(0, 'DISAPPROVED'), (1, 'APPROVED'), (2, 'UNPUBLISHED')]), - ), - migrations.AlterField( - model_name='tag', - name='slug', - field=models.SlugField(blank=True, null=True), - ), - ] diff --git a/posts/migrations/0007_auto_20200601_1934.py b/posts/migrations/0007_auto_20200601_1934.py deleted file mode 100644 index 3cf82cf..0000000 --- a/posts/migrations/0007_auto_20200601_1934.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.0.6 on 2020-06-01 19:34 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('posts', '0006_auto_20200601_1931'), - ] - - operations = [ - migrations.AlterField( - model_name='post', - name='type', - field=models.PositiveSmallIntegerField(choices=[(1, 'TAM ZAMANLI'), (2, 'YARI ZAMANLI'), (3, 'STAJYER'), (4, 'FREELANCE')]), - ), - ] diff --git a/posts/models.py b/posts/models.py index 86705b5..ae0e9d6 100644 --- a/posts/models.py +++ b/posts/models.py @@ -66,7 +66,4 @@ def __str__(self): return self.company.name + ' > ' + self.position def detail(self): - return f"{self.company.id} > {self.company.name}" - - def post_url(self): - return f"http://localhost:8000/posts/{self.id}" + return f"{self.company.id} > {self.company.name}" \ No newline at end of file diff --git a/posts/serializers.py b/posts/serializers.py index 54e7877..c384441 100644 --- a/posts/serializers.py +++ b/posts/serializers.py @@ -44,12 +44,18 @@ class PostSerializer(serializers.ModelSerializer): company = CompanySerializer() tags = TagSerializer(many=True, required=False) + post_url = serializers.SerializerMethodField() class Meta: model = Post fields = ('slug', 'position', 'description', 'apply_url', 'apply_email', 'location', 'type', 'status', 'is_featured', 'pub_date', 'post_url', 'company', 'tags') + def get_post_url(self, obj): + request = self.context.get('request') + return request.build_absolute_uri(f"/posts/slug/{obj.slug}") + + def create(self, validated_data): company_serializer = validated_data['company'] From e1bd83a3ce219d635b17ed51447b10f70af19987 Mon Sep 17 00:00:00 2001 From: tamer Date: Sat, 13 Jun 2020 00:11:15 +0300 Subject: [PATCH 11/12] README.md updated --- README.md | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e8d429a..66eec16 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,35 @@ ### Eksikler -- [ ] Firmaların ilanları eklenecek -- [ ] Etiketlerin ilanları eklenecek -- [ ] Ilan gönderme eklenecek -- [ ] Lokasyona göre ilan arama eklenecek -- [ ] Search eklenecek (Pozisyon adı, şehir, type/örn: tam zamanlı) +- [X] Firmaların ilanları eklenecek +- [X] Etiketlerin ilanları eklenecek +- [X] Ilan gönderme eklenecek +- [X] Lokasyona göre ilan arama eklenecek +- [X] Search eklenecek (Pozisyon adı, şehir, type/örn: tam zamanlı) + +### Gereksinimler + +- postgresql +- python 3.6+ + +### Kurulum + +paketlerin yüklenilmesi +```console +$ pip install -r requirements.txt +``` + +migration oluşturma +```console +$ python manage.py makemigrations +``` + +migrate +```console +$ python manage.py migrate +``` + +superuser oluşturma +```console +$ python manage.py createsuperuser +``` \ No newline at end of file From 472acb2aa32a7a3b2b484c232acbff999ac88e89 Mon Sep 17 00:00:00 2001 From: tamer Date: Sat, 13 Jun 2020 22:41:17 +0300 Subject: [PATCH 12/12] ListCreateAPIView added --- kodilan/urls.py | 1 - posts/models.py | 14 +++++++------- posts/views.py | 9 ++------- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/kodilan/urls.py b/kodilan/urls.py index a1adf50..f303c6a 100644 --- a/kodilan/urls.py +++ b/kodilan/urls.py @@ -28,7 +28,6 @@ path('companies', views.CompaniesView.as_view()), path('posts', views.PostsView.as_view()), path('posts/slug/', views.PostView.as_view()), - path('posts/add', views.CreatePostsView.as_view()), path('posts/activation/', views.ActivatePostView.as_view()), path('locations', views.FindLocationAction.as_view()), path('', include(router.urls)), diff --git a/posts/models.py b/posts/models.py index ae0e9d6..1853081 100644 --- a/posts/models.py +++ b/posts/models.py @@ -35,15 +35,15 @@ def save(self, *args, **kwargs): class Post(models.Model): class TypeEnum(models.IntegerChoices): - FULLTIME = 1, 'TAM ZAMANLI' - PARTTIME = 2, 'YARI ZAMANLI' - TRAINEE = 3, 'STAJYER' - FREELANCER = 4, 'FREELANCE' + FULLTIME = 1, 'Full Time' + PARTTIME = 2, 'Part Time' + TRAINEE = 3, 'Trainee' + FREELANCER = 4, 'Freelancer' class StatusEnum(models.IntegerChoices): - DISAPPROVED = 0, 'ONAYLANMADI' - APPROVED = 1, 'ONAYLANDI' - UNPUBLISHED = 2, 'YAYINLANMADI' + DISAPPROVED = 0, 'Disapproved' + APPROVED = 1, 'Approved' + UNPUBLISHED = 2, 'Unpublished' company = models.ForeignKey(Company, on_delete=models.CASCADE) tags = models.ManyToManyField(Tag) diff --git a/posts/views.py b/posts/views.py index 0b3bdcc..dd2089b 100644 --- a/posts/views.py +++ b/posts/views.py @@ -1,6 +1,6 @@ from .models import Post, Company, Tag from .serializers import PostSerializer, CompanySerializer, TagSerializer, ActivatePostSerializer, LocationSerializer -from rest_framework.generics import ListAPIView, CreateAPIView, RetrieveAPIView +from rest_framework.generics import ListAPIView, ListCreateAPIView, CreateAPIView, RetrieveAPIView from rest_framework import filters from django_filters.rest_framework import DjangoFilterBackend from .filters import PeriodFilterBackend, StatusFilterBackend @@ -14,7 +14,7 @@ class CompaniesView(ListAPIView): ordering_fields = ['id', 'name', 'created_at'] -class PostsView(ListAPIView): +class PostsView(ListCreateAPIView): queryset = Post.objects.all() serializer_class = PostSerializer filter_backends = [StatusFilterBackend, PeriodFilterBackend, DjangoFilterBackend, @@ -31,11 +31,6 @@ class PostView(RetrieveAPIView): filter_backends = [StatusFilterBackend] -class CreatePostsView(CreateAPIView): - queryset = Post.objects.all() - serializer_class = PostSerializer - - class ActivatePostView(RetrieveAPIView): queryset = Post.objects.all() serializer_class = ActivatePostSerializer