WTMO-dev
AI
CV
basic
DL
basic
ML
basic
NLP
basic
dspy
hugging face
llama index
ollama
C++
basic
COMPUTER SCIENCE
basic
ETC
GIT
Blog
basic
IDE
VSC
python
setting
JAVA
basic
MATH
Basic
Statistic
NETWORK
basic
OS
Window
WSL
basic
basic
PROJECT
PYTHON
Advance
Basic
Framework
Django
FastAPI
Library
Module
Home
PROJECT PRACTICE
Crawling
EDA
Kaggle
Python
CV
Contact
Copyright © 2024
WTMO-dev
Home
>
PYTHON
> Framework
Now Loading ...
Framework
Django relations
How to use relations in Django relation은 모델을 더욱 유연하고 연결성이 좋게 만들 수 있습니다. 잘못 설정할 경우 오히려 복잡해지기도 하며, 아래의 방식을 참조하여 정확하게 설정해야 합니다. Django에서는 다음 방식의 relation key가 있습니다. Foriegn Key(many to one) 일반적으로 가장 흔하게 사용되는 관계입니다. 다중값을 가져와야하여 query로 값이 나오게 됩니다. one to one 1대1로 매칭이 되는 상황에서 사용이 가능하며 object를 바로 가져오는 방식이 가능합니다. many to many 관계가 종속적이라기 보다 상호관계를 가지게 되는 형태로 가장 일반적이지 못한 형태입니다. Field of model model의 column들을 정의 하는 field는 다음과 같이 사용할 수 있습니다. Field Option Field의 옵션은 다음과 같으며 모든 Field가 모든 옵션을 사용할 수는 없습니다. Field 옵션 설명 to 필드의 기본값을 설정합니다. related_name 추상 모델에서 관계를 정의할 때 사용될 이름을 의미합니다. on_delete 개체가 제거될 때의 동작을 설정합니다. db_column 데이터베이스의 컬럼의 이름을 설정합니다. limite_choices_to json형식으로 데이터베이스의 컬럼의 옵션을 추가해 filter를 적용합니다. 다음은 on_delete일때 외래키의 작동요건을 나타냅니다. on_delete 의미 models.CASCADE 외래키를 포함하는 행도 함께 삭제 models.PROTECT 해당 요소가 함께 삭제되지 않도록 오류 발생 (ProtectedError) models.SET_NULL 외래키 값을 NULL 값으로 변경 (null=True일 때 사용 가능) models.SET(func) 외래키 값을 func 행동 수행 (func는 함수나 메서드 등을 의미) models.DO_NOTHING 아무 행동을 하지 않음 외래키는 정규참조와 역참조로 이루어지는데 일반적인 방식의 반대가 역참조입니다. 역참조는 일반참조와 달리 사용방법이 달라집니다. related_name이 선언되어 있지 않으면 (1)와 같이 역참조가 가능하며 선언되어 있으면 (2)와 같이 참조가 가능합니다. (2)번 방식이 일반적으로 선호됩니다. 혹시나 2개이상의 참조를 하는 경우 (1)번 방식은 동작을 하지 않기 때문입니다. (1) {% highlight shell %} job1 = .objects.get(id = 1) people = job1._set.all() {% endhighlight %} (2) {% highlight shell %} job1 = .objects.get(id = 1) people = job1..all() {% endhighlight %}
PYTHON
/
Framework
/
Django
· 2024-03-28
Django advance
Advance HttpRequest HttpRequest.headers # request의 headers 객체 HttpRequest.body # request의 body 객체 HttpRequest.COOKIES # 모든 쿠키를 담고 있는 딕셔너리 객체 HttpRequest.method # reqeust의 메소드 타입 HttpRequest.FILES # 로 보낸 UploadFile! HttpRequest.META # HTTP 헤더가 포함하는 모든 접근 가능한 정보를 담고 있는 dict, 메타 정보는 (당연히) web-server에 영향을 받는다. HttpRequest.GET # GET 파라미터를 담고 있는 QueryDict instance HTTpRequest.POST # POST 파라미터를 담고 있는 QueryDict instance queryDict instance Query Expression F function python에서 핸들링 하는것이 아닌 Query로 만들어서 database에서 핸들링을 할 수 있는 방법입니다. {% highlight python %} User.objects.all().update(user_id=F(‘user_id’) + 1) # Base company = Company.objects.annotate(chairs_needed=F(‘num_employees’) - F(‘num_chairs’)) # annotate same field from django.db.models import DateTimeField, ExpressionWrapper, F Ticket.objects.annotate( expires=ExpressionWrapper( F(‘active_at’) + F(‘duration’), output_field=DateTimeField() ) ) # annotate different field {% endhighlight %} Docs Func function Func 방식은 Django에서 구현되지 않은 query의 구문을 사용하고자 할때 활용 가능합니다. {% highlight python %} class UNNEST(Func): function = ‘UNNEST’ temp = Test.objects.annotate(user=UNNEST(‘user’)) {% endhighlight %} Func Q function Q 방식은 Django에서 구현되어 있는 구현체로 조건들을 활용할때 사용하기 좋습니다. {% highlight python %} Product.objects.filter(Q(category=’A’) & Q(sub_category=’AB’)) {% endhighlight %} Value function Value는 기본적인 형태로 단순한 값을 의미합니다. {% highlight python %} User.objects.filter(“user”, Value(“_”), “id”) {% endhighlight %} annotate function annotate는 별칭을 주는것과 같으며 nested와 같은 구조에서 명칭이 복잡할때 사용 가능합니다. {% highlight python %} logs = OrderLog.objects.annotate( name=F(“product__name”), price=F(“product__price”) ).values( ‘created’, ‘name’, ‘price’ ) {% endhighlight %} subquery function subquery는 query를 사용하여 query를 만드는 복잡한 형태의 query를 구성할때 사용합니다. {% highlight python %} from django.db.models import OuterRef, Subquery newest = Comment.objects.filter(post=OuterRef(‘pk’)).order_by(‘-created_at’) Post.objects.annotate(newest_commenter_email=Subquery(newest.values(‘email’)[:1])) {% endhighlight %} Transaction database에서 일관적으로 한번에 작업이 되어야하는 단위를 transaction 이라합니다. 이를 통하여 안정된 서비스를 구현할 수 있으나 과도한 transaction은 오히려 서비스를 느리게 만들 수 있습니다. {% highlight python %} from django.db import transaction @transaction.atomic() def update_user(user_id: int, updated_company_name: str): Profile.objects.filter(user__id=user_id).update(company_name=updated_company_name) User.objects.filter(id=user_id).update(company_name=updated_company_name) {% endhighlight %} commit() transaction을 종료하며 저장합니다. rollback() transaction을 종료하며 처음으로 돌아갑니다. on_commit() transaction commit 종료 이후에 동작이 되야할 경우 사용 savepoint() transaction 도중에 savepoint를 지정하여 commit 또는 rollback같은 작업을 할 수 있습니다. Signal signal은 main logic과 별개로 실행해야하는 작업이 있을때 사용할 수 있습니다. 하지만 비동기적으로 작동되는 로직이 아니라서 celery와 같은것을 활용하는것을 추천합니다. {% highlight python %} 우선 signals.py 를 특정 앱(여기선 user) 안에 만든다. from django.db.models.signals import post_save from django.contrib.auth.models import User def create_profile(sender, instance, created, **kwargs): if created == True: user = instance profile = Profile.objects.create( owner = user, user_name = user.username, email = user.email, name = user.first_name, ) post_save.connect(create_profile, sender=User) 아래에서 더 자세히 보겠지만, connect를 쓰기 싫으면, 아래 app config를 진행하면 된다. signal의 코드 분리를 위해 app config에서, app이 load 될 때 signal을 import하게 한다. (user) apps.py class UserConfig(AppConfig): name = ‘user’ def ready(self) -> None: # DB signal import app.user.signals return super().ready() {% endhighlight %}
PYTHON
/
Framework
/
Django
· 2024-03-27
Django view
What is View How to use View view를 사용하는 방식은 Fuction, Class 두가지 방식이 있습니다. 원래의 Django는 Fuction으로 구성되어 있었으나 OOP의 장점을 이용하는 Class 형식이 추후에 추가 되었습니다. FBV 다음은 Fuction을 사용한 View의 기본 예제입니다. {% highlight python %} @api_view([‘GET’, ‘POST’]) def index(request): if request.method == ‘POST’: return HttpResponse(“Post method”) else: return HttpResponse(“Get method”) {% endhighlight %} CBV 다음은 class를 사용한 View의 기본 예제입니다. 이러한 방식은 상속과 같이 확장성을 가지지만 모든 상황에서 최선은 아닙니다. {% highlight python %} from django.views import View class ContactView(View): def post(self, request): return HttpResponse(“Post method”) def get(self, request): return HttpResponse(“Get method”) {% endhighlight %} {% highlight python %} quickstart/views.py from django.http import HttpResponse, JsonResponse from django.views.decorators.csrf import csrf_exempt from rest_framework.parsers import JSONParser # 그냥 json 파싱만 위해,, @csrf_exempt def snippet_list(request): “”” List all code snippets, or create a new snippet. “”” if request.method == ‘GET’: snippets = Snippet.objects.all() serializer = SnippetSerializer(snippets, many=True) return JsonResponse(serializer.data, safe=False) elif request.method == 'POST': data = JSONParser().parse(request) serializer = SnippetSerializer(data=data) if serializer.is_valid(): serializer.save() return JsonResponse(serializer.data, status=201) return JsonResponse(serializer.errors, status=400) {% endhighlight %} Prev(how to start) Prev
PYTHON
/
Framework
/
Django
· 2024-03-26
Django serializer
What is serializer Django의 serializer는 많은 역활을 담당합니다. 그중 대표적인 사용처는 json <-> dict 형태로의 치환을 주로 해주게 됩니다. 이러한 이유는 rest api에서 사용하는 형식과 python에서 사용하는 형식이 다르기 때문입니다. normal serializer 일반적인 seriallizer는 serializers.Serializer의 상속을 받아 아래와 같이 작성하게 됩니다. {% highlight python %} class UserSerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) name = serializers.CharField(required=False, allow_blank=True, max_length=10) def create(self, validated_data): return models.User.objects.create(**validated_data) {% endhighlight %} 위와 같은 방식은 model과 상관없이 serializer를 설정하고 싶을때 사용할 수 있으며, model을 활용하여 사용하는 방법도 있습니다. model serializer model seriallizer는 serializers.ModelSerializer의 상속을 받아 아래와 같이 작성하게 됩니다. 아래의 방식은 model이 선언되어 있을때 사용하기 용이하며 코드를 간편하게 해줍니다. {% highlight python %} class UserSerializer(serializers.ModelSerializer): class Meta: model = models.User fields = [‘id’, ‘name’] {% endhighlight %} method field method field는 다른 field를 활용하여 재가공하여 사용하는 방식입니다. 대표적으로 id값이 있고 id의 길이를 확인하는 방법입니다. {% highlight python %} class UserSerializer(serializers.ModelSerializer): is_long = serializers.SerializerMethodField() class Meta: model = models.User fields = ['id', 'name'] def get_is_long(self, instance: CheckedCrn): if len(instance.id) > 2: return "long" else: return "short" {% endhighlight %} dict -> json -> dict 때에 따라서는 데이터의 형태가 계속 변경이 되어야 할 수 있습니다. 그에따라 형변환을 하는 법을 나타냅니다. {% highlight python %} import io from rest_framework.renderers import JSONRenderer content = JSONRenderer().render(serializer.data) # json stream = io.BytesIO(content) data = JSONParser().parse(stream) serializer = UserSerializer(data=data) serializer.is_valid() serializer.validated_data # check data serializer.save() serializer.data # dict {% endhighlight %} nested serializer N in (1:N) 1:N 의 형식에서 N의 입장은 다음과 같이 바로 활용이 가능합니다. seriallizer {% highlight python %} class GroupOnlySerializer(serializers.ModelSerializer): class Meta: model = Group fields = “all” class UserWithGroupSerializer(serializers.ModelSerializer): product = GroupOnlySerializer(read_only=True) class Meta: model = User fields = "__all__" {% endhighlight %} view {% highlight python %} class UserListAPIView(generics.ListAPIView): “”” User GET ALL API “”” queryset = User.objects.all().order_by(“-id”) serializer_class = UserWithGroupSerializer {% endhighlight %} 1 in (1:N) 1:N 의 형식에서 1의 입장은 역참조를 해야 하기 때문에 조금은 복잡합니다. Read case seriallizer {% highlight python %} class GroupSerializer(serializers.ModelSerializer): class Meta: model = Group fields = “all” class UserSerializer(serializers.ModelSerializer): product = GroupSerializer(read_only=True, many=True) class Meta: model = User fields = "__all__" {% endhighlight %} view {% highlight python %} class UserListAPIView(generics.ListAPIView): “”” User GET ALL API “”” queryset = User.objects.all().order_by(“-id”) serializer_class = UserSerializer {% endhighlight %} Write case write case에서는 원래 read만 가능하기 때문에 create를 override하여 새로이 구성해줘야 합니다. seriallizer {% highlight python %} class GroupSerializer(serializers.ModelSerializer): class Meta: model = Group fields = “all” class UserSerializer(serializers.ModelSerializer): product = GroupSerializer(read_only=True, many=True) class Meta: model = User fields = "__all__" {% endhighlight %} view {% highlight python %} class UserListAPIView(generics.ListAPIView): “”” User GET ALL API “”” queryset = User.objects.all().order_by(“-id”) serializer_class = UserSerializer def create(self, validated_data): groups = validated_data.pop(‘group’) user = User.objects.create(**validated_data) for group in groups: Group.objects.create(user=user, **group) return user {% endhighlight %} Prev(how to start) Prev
PYTHON
/
Framework
/
Django
· 2024-03-25
Django models
What is model Django에서 model은 DB의 구조를 나타내어집니다. How to use model model의 사용법은 다음과 같습니다. <app-name>.models.py 생성 {% highlight shell %} from django.contrib.auth.models import User from django.db import models class UserDetail(models.Model): user = models.ForeignKey( User, verbose_name=”user 정보”, on_delete=models.DO_NOTHING ) user_email = models.IntegerField(verbose_name=”user email”, null=False, primary_key=True) class Meta: db_table = "userdetail" managed = True verbose_name = "user 상세 정보" verbose_name_plural = "user 상세 정보 목록" {% endhighlight %} <app-name>.admin.py 추가(case 1) {% highlight shell %} from django.contrib import admin from .models import Cart class CartAdmin(admin.ModelAdmin): # admin 페이지에 표기할 column 지정 list_display = (‘', '') admin.site.register(Cart, CartAdmin) # admin page에 등록 {% endhighlight %} <app-name>.admin.py 추가(case 2) {% highlight shell %} from django.contrib import admin from .models import Cart @admin.register(Cart) # admin page에 등록 class CartAdmin(admin.ModelAdmin): # admin 페이지에 표기할 column 지정 list_display = (‘', '') {% endhighlight %} Field of model model의 column들을 정의 하는 field는 다음과 같이 사용할 수 있습니다. ID Field Field 타입 설명 AutoField 1에서 부터 자동적으로 증가합니다. BigAutoField 1 ~ 9223372036854775807까지 1씩 자동으로 증가하는 필드입니다. UUIDField UUID 전용 필드입니다. String Field Field 타입 설명 CharField 적은 문자열을 저장하는 필드입니다. TextField 많은 문자열을 저장하는 필드입니다. URLField URL을 저장하는 필드입니다. EmailField E-mail을 저장하는 필드입니다. String Field Field 타입 설명 CharField 적은 문자열을 저장하는 필드입니다. Data Field Field 타입 설명 BinaryField Binary 데이터를 저장하는 필드입니다. DecimalField Decimal 데이터를 저장하는 필드입니다. IntegerField Interger 데이터를 저장하는 필드입니다. PositiveIntegerField 양수만 취급하는 Interger 데이터를 저장하는 필드입니다. FloatField Float 데이터를 저장하는 필드입니다. BooleanField 참/거짓 데이터를 저장하는 필드입니다. NullBooleanField Null값이 가능한 참/거짓을 저장하는 필드입니다. Time Field Field 타입 설명 DateField 날짜 데이터를 저장하는 필드입니다. TimeField 시간 데이터를 저장하는 필드입니다. DateTimeField 날짜와 시간 데이터 모두 저장할 수 있는 필드입니다. File Field Field 타입 설명 ImageField 이미지 데이터를 저장하는 필드입니다. FileField 파일 업로드 데이터를 저장하는 필드입니다. FilePathField 파일 경로 데이터를 저장하는 필드입니다. Relation Field Field 타입 설명 OneToOneField 일대일 관계를 저장하는 필드입니다. ForeignKey 일대다 관계를 저장하는 필드입니다. ManyToManyField 다대다 관계를 저장하는 필드입니다. 더 많은 정보는 다음에 포함되어 있습니다. relations Field Option Field의 옵션은 다음과 같으며 모든 Field가 모든 옵션을 사용할 수는 없습니다. Field 옵션 설명 기본값 default 필드의 기본값을 설정합니다. - help_text 도움말 텍스트를 설정합니다. - null Null 값 허용 유/무를 설정합니다. False blank 비어있는 값 허용 유/무를 설정합니다. False unique 고유 키 유/무를 설정합니다. False primary_key 기본 키 유/무를 설정합니다. (null=False, unique=True와 동일) False editable 필드 수정 유/무를 설정합니다. False max_length 필드의 최대 길이를 설정합니다. - auto_now 개체가 저장될 때마다 값을 설정합니다. False auto_now_add 개체가 처음 저장될 때 값을 설정합니다. False on_delete 개체가 제거될 때의 동작을 설정합니다. - db_column 데이터베이스의 컬럼의 이름을 설정합니다. - Meta Option Meta 옵션은 다음과 같습니다. Meta 옵션 설명 기본값 abstract 추상 클래스 유/무를 설정합니다. False db_table 모델에 사용할 데이터베이스 테이블의 이름을 설정합니다. - managed 데이터베이스의 생성, 수정, 삭제 등의 권한을 설정합니다. True ordering 객체를 가져올 때의 정렬 순서를 설정합니다. - verbose_name 사람이 읽기 쉬운 객체의 이름을 설정합니다. (단수형으로 작성) - verbose_name_plural 사람이 읽기 쉬운 객체의 이름을 설정합니다. (복수형으로 작성) - model manager model은 기본적으로 objects를 manager로 가지게 되어있습니다. 하지만 아래와 같이 override할 수 있습니다. {% highlight shell %} class DeletedManager(models.Manager): use_for_related_fields = True # default manager로 사용시 관계에서 모두 활용 가능 def deleted(self, **kwargs): return self.filter(is_deleted=False, **kwargs) def get_queryset(self): return super().get_queryset().filter(is_deleted=False) {% endhighlight %} {% highlight shell %} class Post(models.Model): objects = DeletedManager() {% endhighlight %} structure of model 구조는 위에 나열한 Field를 추가하여 주면 되며, Meta를 사용한 abstract기능을 True로 해주면 상속을 할 수 있는 상태가 됩니다. 아래는 default로 있는 User정보의 abstract를 활용한 예시입니다. Mixedin은 2개이상의 상속을 받을때 사용하며, abstract와 같으나 명칭만 다릅니다. {% highlight python %} class User(AbstractBaseUser, PermissionsMixin): objects = UserManager() USERNAME_FIELD = 'email' REQUIRED_FIELDS = ['nickname', ] class Meta: ordering = ('-date_joined',) def __str__(self): # object 호출시 지정할 명칭 return self.nickname {% endhighlight %} migration migration은 DB와 코드간의 상태를 확인하는 작업입니다. 아래의 코드는 migration을 하는데 있어 주로 사용되는 코드입니다. {% highlight shell %} python manage.py showmigrations # migration의 상태확인 python manage.py makemigrations # migration 생성 python manage.py migrate # migration 적용 python manage.py –fake zero # 의 migration 초기화 python manage.py --fake-initial # fake migration 생성 {% endhighlight %}
PYTHON
/
Framework
/
Django
· 2024-03-22
Django rest api
How to start Django restfulAPI Django를 활용하여 REST API를 만드는데 최적화 되어 있는 모듈입니다. 다음의 방식으로 다운받을 수 있습니다. {% highlight shell %} pip install djangorestframework pip install markdown # Markdown support for the browsable API. pip install django-filter # Filtering support {% endhighlight %} setting.py {% highlight python %} INSTALLED_APPS = [ # default setting …, ‘rest_framework’, ] REST_FRAMEWORK = [ # additional pagination setting ‘DEFAULT_PAGINATIOM_CLASS’: ‘rest_framework.pagination.pageNumberPagination’, ‘PAGE_SIZE’: 50 ] {% endhighlight %} Next step models serializers views Prev(how to start) Prev
PYTHON
/
Framework
/
Django
· 2024-03-21
Django additional module
What is Django debug toolbar Django의 webpage들에서 debug 정보를 바로 확인하기 위한 middleware입니다. How to setting 다음은 Django debug toolbar를 설치하는 방법입니다. django-debug-toolbar 설치 {% highlight shell %} pip install django-debug-toolbar {% endhighlight %} <proj-name>.settings.py에서 확인, 추가 {% highlight python %} INSTALLED_APPS = [ # … “django.contrib.staticfiles”, # … ] STATIC_URL = “static/” {% endhighlight %} <proj-name>.settings.py에서 확인, 추가 {% highlight python %} TEMPLATES = [ { “BACKEND”: “django.template.backends.django.DjangoTemplates”, “APP_DIRS”: True, # … } ] {% endhighlight %} <proj-name>.settings.py에 추가 {% highlight python %} INSTALLED_APPS = [ # … “debug_toolbar”, # … ] {% endhighlight %} <proj-name>.urls.py에 추가 {% highlight python %} from django.conf import settings if settings.DEBUG: import mimetypes mimetypes.add_type(“application/javascript”, “.js”, True) import debug_toolbar urlpatterns += [ path(r’^debug/’, include(debug_toolbar.urls)), ] {% endhighlight %} <proj-name>.settings.py에 추가 {% highlight python %} MIDDLEWARE = [ # … “debug_toolbar.middleware.DebugToolbarMiddleware”, # … ] {% endhighlight %} <proj-name>.settings.py에 추가 {% highlight python %} INTERNAL_IPS = [ # … “127.0.0.1”, # ... ] {% endhighlight %} 위의 방식을 전부 완료하고 나서 Django proj를 실행하면 오른쪽에 debug toolbar가 보여집니다. 혹시나 보여지지 않으시다면 아래의 코드를 추가해주세요. <proj-name>.urls.py에 추가 {% highlight python %} import mimetypes mimetypes.add_type(“application/javascript”, “.js”, True) {% endhighlight %} What is Django celery Django에서 celery를 활용하여 worker와 broker를 사용하는 기능입니다. How to setting 다음은 Django celery(W.redis)를 설치하는 방법입니다. <proj-name>.celery.py 생성 {% highlight python %} import os from celery import Celery Set the default Django settings module for the ‘celery’ program. os.environ.setdefault(‘DJANGO_SETTINGS_MODULE’, ‘mysite.settings’) app = Celery(‘projs’) Using a string here means the worker doesn’t have to serialize the configuration object to child processes. - namespace=’CELERY’ means all celery-related configuration keys should have a CELERY_ prefix. app.config_from_object(‘django.conf:settings’, namespace=’CELERY’) Load task modules from all registered Django apps. app.autodiscover_tasks() @app.task(bind=True, ignore_result=True) def debug_task(self): print(f’Request: {self.request!r}’) {% endhighlight %} <proj-name>.settings.py에 추가 {% highlight python %} CELERY_BROKER_URL = ‘redis://127.0.0.1:6379/0’ CELERY_RESULT_BACKEND = ‘redis://127.0.0.1:6379/0’ CELERY_ACCEPT_CONTENT = [‘application/json’] CELERY_RESULT_SERIALIZER = ‘json’ CELERY_TASK_SERIALIZER = ‘json’ CELERY_TIMEZONE = ‘Asia/Seoul’ CELERY_BROKER_CONNECTION_RETRY_ON_STARTUP = True {% endhighlight %} <proj-name>.__init__.py에 추가 {% highlight python %} from .celery import app as celery_app all = (‘celery_app’,) {% endhighlight %} shared_task 데코레이터를 사용한 함수 delay로 실행 {% highlight python %} from celery import shared_task {% endhighlight %} 위와 같이 코드를 완성하고 나서 다음의 코드로 celery를 구동시키면 사용이 가능합니다. {% highlight shell %} celery -A worker -l INFO {% endhighlight %}
PYTHON
/
Framework
/
Django
· 2024-03-21
Django intro
What is Django Django python으로 구현된 web framework입니다. Getting start 다음은 아주 기본적인 django 의 실행방식입니다. {% highlight shell %} pip install Django # Django 설치 python -m django –version # Django 설치 확인 django-admin startproject # 프로젝트 생성 cd python manage.py runserver # 프로젝트 실행 {% endhighlight %} 다음은 startproject로 생성되는 파일의 구조입니다. <proj-name>/ manage.py <proj-name>/ __init__.py settings.py urls.py asgi.py wsgi.py Creating App 다음은 django에서 App을 만들고 연결하는 방법입니다. {% highlight shell %} python manage.py startapp # create App {% endhighlight %} 다음은 startapp으로 생성되는 dir의 구조 입니다. <app-name>/ __init__.py admin.py apps.py migrations/ __init__.py models.py tests.py urls.py views.py <app-name>/views.py {% highlight python %} from django.http import HttpResponse def index(request): return HttpResponse(“Hello, world. You’re at the polls index.”) {% endhighlight %} <app-name>/urls.py {% highlight python %} from django.urls import path from . import views urlpatterns = [ path(“”, views.index, name=”index”), ] {% endhighlight %} <proj-name>/urls.py {% highlight python %} from django.contrib import admin from django.urls import include, path urlpatterns = [ path(“/", include(".urls")), path("admin/", admin.site.urls), ] {% endhighlight %} Setting database 다음은 django에서 database를 세팅하는 방법입니다. Django는 기본으로 SQLite를 활용하지만 설정을 변경하여 수정이 가능합니다. mysql 다음은 mysql 예시입니다. 필수 모듈 설치 {% highlight shell %} pip install mysqlclient {% endhighlight %} <proj-name>.settings.py에 DB 옵션변경 {% highlight python %} DATABASES = { ‘default’: { ‘ENGINE’: ‘django.db.backends.mysql’, ‘NAME’: ‘', 'USER': '', 'PASSWORD': '', 'HOST': 'localhost', 'PORT': '3306', } } {% endhighlight %} postgre 다음은 postgre 예시입니다. 필수 모듈 설치 {% highlight shell %} pip install psycopg2 {% endhighlight %} postgre setting {% highlight shell %} sudo su - postgres # postgre 실행 psql CREATE DATABASE django_test; # postgre 유저 및 DB 생성 CREATE USER django_user WITH PASSWORD ‘django_pass’; ALTER ROLE django_user SET client_encoding TO ‘utf8’; ALTER ROLE django_user SET default_transaction_isolation TO ‘read committed’; ALTER ROLE django_user SET timezone TO ‘UTC’; GRANT ALL PRIVILEGES ON DATABASE django_test TO django_user; \q {% endhighlight %} <proj-name>.settings.py에 DB 옵션변경 {% highlight python %} DATABASES = { ‘default’: { ‘ENGINE’: ‘django.db.backends.mysql’, ‘NAME’: ‘', 'USER': '', 'PASSWORD': '', 'HOST': 'localhost', 'PORT': '3306', } } {% endhighlight %} Next(Web page) Next Next(Rest API) Next
PYTHON
/
Framework
/
Django
· 2024-03-20
FastAPI Advance
Advance 준비중입니다. Advance
PYTHON
/
Framework
/
FastAPI
· 2024-02-08
FastAPI SQL
SQL SQL은 다음과 같이 구조화 하여 사용할 수 있습니다. sql_app/__init__.py: empty sql_app/database.py sql_app/models.py sql_app/schemas.py sql_app/crud.py sql_app/main.py sql_app/database.py database 파일은 다음과 같이 구성됩니다. {% highlight python %} from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker SQLALCHEMY_DATABASE_URL = “sqlite:///./sql_app.db” SQLALCHEMY_DATABASE_URL = “postgresql://user:password@postgresserver/db” engine = create_engine( SQLALCHEMY_DATABASE_URL, connect_args={“check_same_thread”: False} ) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) Base = declarative_base() {% endhighlight %} sql_app/models.py models 파일은 다음과 같이 구성됩니다. {% highlight python %} from sqlalchemy import Boolean, Column, ForeignKey, Integer, String from sqlalchemy.orm import relationship from .database import Base class User(Base): tablename = “users” id = Column(Integer, primary_key=True) email = Column(String, unique=True, index=True) hashed_password = Column(String) is_active = Column(Boolean, default=True) items = relationship("Item", back_populates="owner") class Item(Base): tablename = “items” id = Column(Integer, primary_key=True) title = Column(String, index=True) description = Column(String, index=True) owner_id = Column(Integer, ForeignKey("users.id")) owner = relationship("User", back_populates="items") {% endhighlight %} sql_app/schemas.py schemas 파일은 다음과 같이 구성됩니다. {% highlight python %} from typing import Union from pydantic import BaseModel class ItemBase(BaseModel): title: str description: Union[str, None] = None class ItemCreate(ItemBase): pass class Item(ItemBase): id: int owner_id: int class Config: orm_mode = True class UserBase(BaseModel): email: str class UserCreate(UserBase): password: str class User(UserBase): id: int is_active: bool items: list[Item] = [] class Config: orm_mode = True {% endhighlight %} sql_app/crud.py crud 파일은 다음과 같이 구성됩니다. {% highlight python %} from sqlalchemy.orm import Session from . import models, schemas def get_user(db: Session, user_id: int): return db.query(models.User).filter(models.User.id == user_id).first() def get_user_by_email(db: Session, email: str): return db.query(models.User).filter(models.User.email == email).first() def get_users(db: Session, skip: int = 0, limit: int = 100): return db.query(models.User).offset(skip).limit(limit).all() def create_user(db: Session, user: schemas.UserCreate): fake_hashed_password = user.password + “notreallyhashed” db_user = models.User(email=user.email, hashed_password=fake_hashed_password) db.add(db_user) db.commit() db.refresh(db_user) return db_user def get_items(db: Session, skip: int = 0, limit: int = 100): return db.query(models.Item).offset(skip).limit(limit).all() def create_user_item(db: Session, item: schemas.ItemCreate, user_id: int): db_item = models.Item(**item.dict(), owner_id=user_id) db.add(db_item) db.commit() db.refresh(db_item) return db_item {% endhighlight %} sql_app/main.py main 파일은 다음과 같이 구성됩니다. {% highlight python %} from fastapi import Depends, FastAPI, HTTPException from sqlalchemy.orm import Session from . import crud, models, schemas from .database import SessionLocal, engine models.Base.metadata.create_all(bind=engine) app = FastAPI() Dependency def get_db(): db = SessionLocal() try: yield db finally: db.close() @app.post(“/users/”, response_model=schemas.User) def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)): db_user = crud.get_user_by_email(db, email=user.email) if db_user: raise HTTPException(status_code=400, detail=”Email already registered”) return crud.create_user(db=db, user=user) @app.get(“/users/”, response_model=list[schemas.User]) def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): users = crud.get_users(db, skip=skip, limit=limit) return users @app.get(“/users/{user_id}”, response_model=schemas.User) def read_user(user_id: int, db: Session = Depends(get_db)): db_user = crud.get_user(db, user_id=user_id) if db_user is None: raise HTTPException(status_code=404, detail=”User not found”) return db_user @app.post(“/users/{user_id}/items/”, response_model=schemas.Item) def create_item_for_user( user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db) ): return crud.create_user_item(db=db, item=item, user_id=user_id) @app.get(“/items/”, response_model=list[schemas.Item]) def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): items = crud.get_items(db, skip=skip, limit=limit) return items {% endhighlight %} W. middleware 다음은 미들웨어를 사용한 예시를 제시합니다. {% highlight python %} @app.middleware(“http”) async def db_session_middleware(request: Request, call_next): response = Response(“Internal server error”, status_code=500) try: request.state.db = SessionLocal() response = await call_next(request) finally: request.state.db.close() return response Dependency def get_db(request: Request): return request.state.db {% endhighlight %}
PYTHON
/
Framework
/
FastAPI
· 2024-02-07
FastAPI types
Enum Enum 클래스는 다음과 같이 사용할 수 있습니다. {% highlight python %} from enum import Enum class ModelName(str, Enum): alexnet = “alexnet” resnet = “resnet” lenet = “lenet” {% endhighlight %} 값을 사용하는 방식은 다음과 같이 사용 가능합니다. model.value Model.<key> {% highlight python %} app = FastAPI() @app.get(“/models/{model_name}”) async def get_model(model_name: ModelName): if model_name is ModelName.alexnet: {% endhighlight %} Optional Optional type의 경우 None일 수 있는 데이터를 의미하며 다음과 같이 작성할 수 있습니다. {% highlight python %} from typing import Optional app = FastAPI() @app.get(“/items/{item_id}”) async def read_item(item_id: Optional[int]): return {“item_id”: item_id} {% endhighlight %} httpUrl httpUrl type의 경우 url형식을 받을 수 있는 형식입니다. {% highlight python %} from fastapi import FastAPI from pydantic import BaseModel, HttpUrl app = FastAPI() class Image(BaseModel): url: HttpUrl name: str @app.put(“/items”) async def update_item(image: Image): results = {“image”: image} return results {% endhighlight %} output type output type의 경우 일반적인 방식으로 활용이 가능합니다. response_model=None은 2종류 이상의 타입이 가능할경우 pydantic의 규제를 피할때 사용할 수 있습니다. response_model_exclude_unset=True은 output을 넘겨줄때 pydantic의 default value는 무시하는 방법입니다. response_model_exclude={""}은 output을 넘겨줄때 넘겨주지 않을 value를 지정하는 방법입니다. {% highlight python %} from fastapi import FastAPI, Response from fastapi.responses import RedirectResponse app = FastAPI() @app.get(“/portal”, response_model=None, response_model_exclude_unset=True, response_model_exclude={“tax”}) async def get_portal(teleport: bool = False) -> Response | dict: if teleport: return RedirectResponse(url=”https://www.youtube.com/watch?v=dQw4w9WgXcQ”) return {“message”: “Here’s your interdimensional portal.”} {% endhighlight %} and others 이외에도 다양한 data type이 존재하며 다음과 같습니다. datetime.datetime datetime.date datetime.time datetime.timedelta frozenset bytes Decimal schema examples pydantic으로 형식을 만들때는 다음과 같이 예시문을 만들 수 있습니다. 예시문을 활용하면 docs에서 예시문을 확인할 수 있습니다. {% highlight python %} class Item(BaseModel): name: str description: str | None = None price: float tax: float | None = None model_config = { "json_schema_extra": { "examples": [ { "name": "Foo", "description": "A very nice Item", "price": 35.4, "tax": 3.2, } ] } } {% endhighlight %} pydantic이 아닐경우는 Body, Query, …에서 직접 예시문을 작성할 수도 있습니다. {% highlight python %} Body( examples=[ { “name”: “Foo”, “description”: “A very nice Item”, “price”: 35.4, “tax”: 3.2, } ], ), {% endhighlight %}
PYTHON
/
Framework
/
FastAPI
· 2024-02-06
FastAPI intro Num.3
Security API를 만드는데 있어서 보안사항은 매우 중요합니다. 아래의 순서를 따라 보안을 설정할 수 있습니다. {% highlight shell %} pip install python-jose # jwt token module pip install passlib[bcrypt] # password hashing module {% endhighlight %} 만약 (trapped) error reading bcrypt version과 같은 문제가 발생하면 아래의 코드를 작동시켜야 합니다. {% highlight shell %} pip install bcrypt==4.0.1 {% endhighlight %} 아래의 방법은 보안을 설정하는 방법들을 제안합니다. {% highlight python %} from datetime import datetime, timedelta, timezone from typing import Annotated, Union from fastapi import Depends, FastAPI, HTTPException, status from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from jose import JWTError, jwt from passlib.context import CryptContext from pydantic import BaseModel to get a string like this run: openssl rand -hex 32 SECRET_KEY = “09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7” ALGORITHM = “HS256” ACCESS_TOKEN_EXPIRE_MINUTES = 30 fake_users_db = { “johndoe”: { “username”: “johndoe”, “full_name”: “John Doe”, “email”: “johndoe@example.com”, “hashed_password”: “$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW”, “disabled”: False, } } class Token(BaseModel): access_token: str token_type: str class TokenData(BaseModel): username: Union[str, None] = None class User(BaseModel): username: str email: Union[str, None] = None full_name: Union[str, None] = None disabled: Union[bool, None] = None class UserInDB(User): hashed_password: str pwd_context = CryptContext(schemes=[“bcrypt”], deprecated=”auto”) oauth2_scheme = OAuth2PasswordBearer(tokenUrl=”token”) app = FastAPI() def verify_password(plain_password, hashed_password): return pwd_context.verify(plain_password, hashed_password) def get_password_hash(password): return pwd_context.hash(password) def get_user(db, username: str): if username in db: user_dict = db[username] return UserInDB(**user_dict) def authenticate_user(fake_db, username: str, password: str): user = get_user(fake_db, username) if not user: return False if not verify_password(password, user.hashed_password): return False return user def create_access_token(data: dict, expires_delta: Union[timedelta, None] = None): to_encode = data.copy() if expires_delta: expire = datetime.now(timezone.utc) + expires_delta else: expire = datetime.now(timezone.utc) + timedelta(minutes=15) to_encode.update({“exp”: expire}) encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) return encoded_jwt async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]): credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail=”Could not validate credentials”, headers={“WWW-Authenticate”: “Bearer”}, ) try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) username: str = payload.get(“sub”) if username is None: raise credentials_exception token_data = TokenData(username=username) except JWTError: raise credentials_exception user = get_user(fake_users_db, username=token_data.username) if user is None: raise credentials_exception return user async def get_current_active_user( current_user: Annotated[User, Depends(get_current_user)], ): if current_user.disabled: raise HTTPException(status_code=400, detail=”Inactive user”) return current_user @app.post(“/token”) async def login_for_access_token( form_data: Annotated[OAuth2PasswordRequestForm, Depends()], ) -> Token: user = authenticate_user(fake_users_db, form_data.username, form_data.password) if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail=”Incorrect username or password”, headers={“WWW-Authenticate”: “Bearer”}, ) access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) access_token = create_access_token( data={“sub”: user.username}, expires_delta=access_token_expires ) return Token(access_token=access_token, token_type=”bearer”) @app.get(“/users/me/”, response_model=User) async def read_users_me( current_user: Annotated[User, Depends(get_current_active_user)], ): return current_user @app.get(“/users/me/items/”) async def read_own_items( current_user: Annotated[User, Depends(get_current_active_user)], ): return [{“item_id”: “Foo”, “owner”: current_user.username}] {% endhighlight %} Middleware 미들웨어는 다양한 작동에 있어서 중간에 행동을 하는 행동입니다. 현재 fastapi는 http protocol이 진행되는 시점의 middleware만 사용이 가능합니다. 사용방법은 다음 두가지 방법을 활용할 수 있습니다. {% highlight python %} class CustomMiddleware(BaseHTTPMiddleware): def __init__(self, app): super().__init__(app) async def dispatch(self, request, call_next): response = await call_next(request) return response app.add_middleware(CustomMiddleware) {% endhighlight %} {% highlight python %} @app.middleware(“http”) async def add_process_time_header(request: Request, call_next): start_time = time.time() response = await call_next(request) process_time = time.time() - start_time response.headers[“X-Process-Time”] = str(process_time) return response {% endhighlight %} advance middleware CORS CORS는 Cross-Origin-Resource-sharing의 약자로 API를 호출하는 사용자의 ip를 관리하는 방법입니다. {% highlight python %} from fastapi.middleware.cors import CORSMiddleware origins = [ “http://localhost”, “http://localhost:8080”, ] app.add_middleware( CORSMiddleware, allow_origins=origins, allow_credentials=True, allow_methods=[“”], allow_headers=[“”], ) {% endhighlight %} Background Task main logic이 진행되는 것과 별개로 Background Task를 동작시킬 수 있습니다. 이렇게 동작되면 main logic에 영향을 주지 않는 동작을 빠르게 실행 할 수 있습니다. {% highlight python %} from fastapi import BackgroundTasks, FastAPI app = FastAPI() def write_log(message: str): with open(“log.txt”, mode=”a”) as log: log.write(message) @app.post(“/send-notification/{email}”) async def send_notification( email: str, background_tasks: BackgroundTasks ): message = f”message to {email}\n” background_tasks.add_task(write_log, message) return {“message”: “Message sent”} {% endhighlight %} Info fastapi는 기본적인 info를 지정 할 수 있습니다. 이는 docs를 통하여 보여지며 아래와 같이 작성이 가능합니다. {% highlight python %} app = FastAPI( title=”ChimichangApp”, description=description, summary=”Deadpool’s favorite app. Nuff said.”, version=”0.0.1”, terms_of_service=”http://example.com/terms/”, contact={ “name”: “Deadpoolio the Amazing”, “url”: “http://x-force.example.com/contact/”, “email”: “dp@x-force.example.com”, }, license_info={ “name”: “Apache 2.0”, “identifier”: “MIT”, }, ) {% endhighlight %} static files fastapi로 static file을 사용할 수 있습니다. 이러한 기능을 통하여 간단한 소개글이나 홈페이지 같은것도 제작이 가능해집니다. {% highlight python %} from fastapi import FastAPI from fastapi.staticfiles import StaticFiles app = FastAPI() app.mount(“/static”, StaticFiles(directory=”static”), name=”static”) {% endhighlight %} API Test fastapi를 작성하고나면 정상적으로 작성이 되었는지 걱정이 될 수 있습니다. 이럴때 test를 해볼 수 있는 기능을 지원합니다. 아래와 같이 작성 후 pytest를 통하여 확인을 해볼 수 있습니다. test_*.py {% highlight python %} from fastapi import FastAPI from fastapi.testclient import TestClient app = FastAPI() @app.get(“/”) async def read_main(): return {“msg”: “Hello World”} client = TestClient(app) def test_read_main(): response = client.get(“/”) assert response.status_code == 200 assert response.json() == {“msg”: “Hello World”} {% endhighlight %} run uvicorn(W. code) 다음과 같이 코드상에서 uvicorn을 작동하면 코드를 관리하는데 더욱 장점을 가질 수 있습니다. {% highlight python %} import uvicorn from fastapi import FastAPI app = FastAPI() @app.get(“/”) def root(): return {“value”: “hello world”} if name == “main”: uvicorn.run(app, host=”0.0.0.0”, port=8000) {% endhighlight %} Prev Prev
PYTHON
/
Framework
/
FastAPI
· 2024-02-05
FastAPI intro Num.2
Error handling API를 만드는데 있어서 Error 상황이 있을 수 있습니다. 이런경우 error 상태를 요청자에게 안내와 함께 시스템의 결과 관리를 해줘야 합니다. HTTPException 가장 일반적인 exception으로 아래와 같이 사용 가능합니다. 일반적으로 headers를 작성할 필요가 없으나 Token을 만들거나 하는 상황이나 보안적인 문제가 있을때 사용하기에 좋습니다. {% highlight python %} from fastapi import FastAPI, HTTPException app = FastAPI() items = {“foo”: “The Foo Wrestlers”} @app.get(“/items/{item_id}”) async def read_item(item_id: str): if item_id not in items: raise HTTPException( status_code=404, detail=”Item not found”, headers={“X-Error”: “There goes my error”}, ) return {“item”: items[item_id]} {% endhighlight %} Custom Exception Exception은 다음과 같이 custom해서 사용 가능합니다. {% highlight python %} from fastapi import FastAPI, Request from fastapi.responses import JSONResponse class UnicornException(Exception): def init(self, name: str): self.name = name app = FastAPI() @app.exception_handler(UnicornException) async def unicorn_exception_handler(request: Request, exc: UnicornException): return JSONResponse( status_code=418, content={“message”: f”Oops! {exc.name} did something. There goes a rainbow…”}, ) @app.get(“/unicorns/{name}”) async def read_unicorn(name: str): if name == “yolo”: raise UnicornException(name=name) return {“unicorn_name”: name} {% endhighlight %} 다음 보여드릴 방식은 기본 설정된 starlette, RequestValidation을 custom해서 추가 기능을 구현하려고 할때 활용 가능합니다. {% highlight python %} from fastapi import FastAPI from fastapi.exception_handlers import ( http_exception_handler, request_validation_exception_handler, ) from fastapi.exceptions import RequestValidationError from starlette.exceptions import HTTPException as StarletteHTTPException app = FastAPI() @app.exception_handler(StarletteHTTPException) async def custom_http_exception_handler(request, exc): print(f”OMG! An HTTP error!: {repr(exc)}”) return await http_exception_handler(request, exc) @app.exception_handler(RequestValidationError) async def validation_exception_handler(request, exc): print(f”OMG! The client sent invalid data!: {exc}”) return await request_validation_exception_handler(request, exc) {% endhighlight %} Routing routing을 하는 방식은 크게 2가지를 볼 수 있습니다. use tags {% highlight python %} from fastapi import FastAPI app = FastAPI() @app.get(“/items/”, tags=[“items”]) async def create_item(): return None {% endhighlight %} use router {% highlight python %} from fastapi import FastAPI, APIRouter app = FastAPI() router = APIRouter(prefix=’/slack’, tags=[“items”]) app.include_router(user.router) @router.get(“/items/”) async def create_item(): return None {% endhighlight %} JsonEncoder pydantic으로 인하여 dict형이 아니거나 2개이상의 dict형을 융합하여 활용하고 싶을때 사용할 수 있는 방법입니다. {% highlight python %} from fastapi.encoders import jsonable_encoder from pydantic import BaseModel class Item(BaseModel): title: str timestamp: datetime description: str | None = None @app.put(“/items/{id}”) def update_item(id: str, item: Item): jsonable_encoder(item) {% endhighlight %} Dependencies Dependencies는 FastAPI가 동작하는데 있어서 종속성을 주입하는 것입니다. 이는 더욱 안정적으로 동작할 수 있게 해줍니다. pydantic으로 사용하는것을 권장하나 아래와 같이 사용할 수 도 있습니다. Function Dependencies 다음과 같이 함수형으로도 만들 수 있습니다. {% highlight python %} from typing import Annotated from fastapi import Depends, FastAPI app = FastAPI() async def common_parameters( skip: int = 0, limit: int = 100 ): return {“skip”: skip, “limit”: limit} @app.get(“/items/”) async def read_items(commons: Annotated[dict, Depends(common_parameters)]): return commons {% endhighlight %} Class Dependencies 다음과 같이 클래스형으로 만들 수 있습니다. {% highlight python %} from typing import Annotated, Union from fastapi import Depends, FastAPI app = FastAPI() class CommonQueryParams: def init(self, skip: int = 0, limit: int = 100): self.skip = skip self.limit = limit @app.get(“/items/”) async def read_items(commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]): return commons {% endhighlight %} Path Operation Dependencies 다음과 같이 함수의 입력이 아니라 path operation dependency에 입력하여 값을 검증 할 수도 있습니다. {% highlight python %} from typing import Annotated from fastapi import Depends, FastAPI, Header, HTTPException app = FastAPI() async def verify_token(x_token: Annotated[str, Header()]): if x_token != “fake-super-secret-token”: raise HTTPException(status_code=400, detail=”X-Token header invalid”) @app.get(“/items/”, dependencies=[Depends(verify_token)]) async def read_items(): return None {% endhighlight %} Global Dependencies 전역 Dependency도 사용할 수 있습니다. 이는 token 검증과 같은 활동에 있어서 유용합니다. {% highlight python %} app = FastAPI(dependencies=[Depends(verify_token), Depends(verify_key)]) {% endhighlight %} Dependencies using yield Dependency를 사용하여 DB의 무결성도 확인이 가능합니다. 아래의 예시는 sqlalchemy를 사용하였으나 다음과 같이 구성하면 활용성을 향상시킬 수 있습니다. {% highlight python %} from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine( , pool_pre_ping=True ) Session = sessionmaker(autocommit=False, autoflush=False, bind=engine) def get_db(): db = Session() try: yield db finally: db.close() def router(s: Annotated[Service, Depends(get_db)]): s.action() {% endhighlight %} {% highlight python %} class MySuperContextManager: def init(self): self.db = DBSession() def __enter__(self): return self.db def __exit__(self, exc_type, exc_value, traceback): self.db.close() async def get_db(): with MySuperContextManager() as db: yield db {% endhighlight %} 아래의 로직을 이해한다면 fastapi를 자유롭게 사용하는데 문제가 없다고 볼 수 있습니다. Prev Prev Next Next
PYTHON
/
Framework
/
FastAPI
· 2024-02-02
FastAPI intro Num.1
What is FastAPI FastAPI는 python으로 구현된 web framework입니다. FastAPI는 빠르게 개발이 가능하고 python으로 구현된 web framework중 가장 빠르다고 이야기 되고 있습니다. type check를 해주는 pydantic과 Asynchronous Server Gateway interface로 구현된 starlette가 구성되어 있습니다. 그에 따라 다음과 같은 장점을 가지게 됩니다. type check 비동기적 작업 지원 fast to create docs FastAPI는 docs를 자체적으로 지원을 해줘서 빠른 문서작업이 가능해집니다. FastAPI는 Swagger UI를 기반으로한 API docs를 지원해줍니다. 해당 자료는 FastAPI를 구동하면 https://localhost:port/docs에서 확인을 할 수 있습니다. FastAPI는 ReDoc을 기반으로한 API docs 또한 지원을 해줍니다. 해당 자료는 FastAPI 구동이후 https://localhost:port/redoc에서 확인을 할 수 있습니다. How to start FastAPI FastAPI를 사용하기 위하여 우선 두가지 모듈을 다운 받아야합니다. {% highlight shell %} pip install fastapi pip install uvicorn {% endhighlight %} 이후 실행을 위하여 아래의 코드 작성 및 실행이 필요합니다. main.py {% highlight python %} from fastapi import FastAPI app = FastAPI() @app.get(“/”) async def root(): return {“message”: “Hello World”} {% endhighlight %} you should run in cli {% highlight shell %} uvicorn main:app –reload {% endhighlight %} main:app은 main.py에 있는 app을 실행한다는 의미입니다. Input Datas API의 데이터 입출력을 위하여 다양한 방식의 input data가 존재합니다. 아래는 해당 값들을 순차적으로 설명드리겠습니다. Path Parameters 경로 파라미터라고 불리는 것으로 아래의 코드에서 item_id를 의미합니다. FastAPI는 type에 있어서 매우 엄격합니다. 그래서 아래의 코드와 같이 type hint(or pydantic)가 없거나 일치 하지 않을 경우 작동하지 않습니다. 주소창에 입력하는 값으로보면 숫자를 입력해도 문자로 보일 수 있으나 type hint에 따라서 숫자로 인식됩니다. {% highlight python %} @app.get(“/items/{item_id}”) async def read_item(item_id: int): return {“item_id”: item_id} {% endhighlight %} 위와 같은 주소체계를 사용할때 주의 할점은 "/items/item"이라고 만약 새로운 주소체계를 선언할 경우 path parameter로 구성된 주소체계보다 상단에 존재하여야합니다. 이는 중복된 입력으로 판단이 될 수 있기 때문입니다. Path Parameter가 "/"와 같이 경로 데이터가 포함이 되어있을 경우 starlette에서 지원해주는 type check 기능으로 :path를 추가해 아래와 같이 활용 가능합니다. {% highlight python %} @app.get(“/items/{item_id:path}”) async def read_item(item_id: str): return {“item_id”: item_id} {% endhighlight %} Path Parameter는 아래와 같이 상세한 유효성 검사를 지원하며 사용법은 다음과 같습니다. gt: 보다 크다. ge: 크거나 같다. lt: 보다 작다. le: 작거나 같다. {% highlight python %} from typing import Annotated from fastapi import FastAPI, Path app = FastAPI() @app.get(“/items/{item_id}”) async def read_items( q: str, item_id: Annotated[int, Path(title=”The ID of the item to get”)] ): results = {“item_id”: item_id} if q: results.update({“q”: q}) return results {% endhighlight %} Query Parameters Path Parameter이외에 입력값을 작성하면 Query Parameter로 인식됩니다. url로 값을 직접 전송하려면 URL/?data=data와 같이 전송이 가능합니다. Query Parameter의 경우 default값을 제공해주지 않거나 Ellipsis를 주면 필수 입력 조건으로 사용이됩니다. Query Parameter는 다음과 같이 유효성 검사를 추가적으로 진행 할 수 있습니다. 이것이 의미하는 바는 q는 str인데 None일 수 있으며 최대 글자수가 50이 넘으면 안되는 기본값 None을 가진 parameter라고 볼 수 있습니다. {% highlight python %} from typing import Annotated from fastapi import FastAPI, Query app = FastAPI() @app.get(“/items/”) async def read_items(q: Annotated[str | None, Query(max_length=50)] = None): results = {“items”: [{“item_id”: “Foo”}, {“item_id”: “Bar”}]} if q: results.update({“q”: q}) return results {% endhighlight %} Query에는 다음과 같이 다양한 입력을 줄 수 있습니다. alias: 입력 명칭을 변경 deprecated: 사용 가능 여부 include_in_schema: 문서에 표현 여부 {% highlight python %} Query( alias=”item-query”, title=”Query string”, description=”Query string for the items to search in the database that have a good match”, min_length=3, max_length=50, pattern=”^fixedquery$”, deprecated=True, include_in_schema=False ), {% endhighlight %} Body 다중 입력값이 존재할경우 흔하게 사용되는 방식입니다. fastapi에서 지원하는 Request를 사용해도 가능하지만 pydantic을 이용하는것이 무결성에 있어서 안전합니다. {% highlight python %} from fastapi import FastAPI from pydantic import BaseModel class Item(BaseModel): name: str description: str | None = None price: float tax: float | None = None app = FastAPI() @app.post(“/items/”) async def create_item(item: Item): return item {% endhighlight %} Single Body Body를 사용하는데 있어서 단일값일 경우 아래와 같이 사용이 가능합니다. {% highlight python %} from typing import Annotated from fastapi import Body, FastAPI app = FastAPI() @app.put(“/items”) async def update_item( importance: Annotated[int, Body()] ): return {“importance”: importance} {% endhighlight %} Body Field body도 query와 같이 Field를 사용하여 추가적인 무결성 검사를 할 수 있습니다. {% highlight python %} from typing import Annotated from fastapi import Body, FastAPI from pydantic import BaseModel, Field app = FastAPI() class Item(BaseModel): price: float = Field(gt=0, description=”The price must be greater than zero”) tax: float @app.put(“/items”) async def update_item(item: Annotated[Item, Body(embed=True)]): results = {“item_id”: item_id, “item”: item} return results {% endhighlight %} Cookie Cookie 데이터가 있을경우 아래와 같이 사용할 수 있습니다. {% highlight python %} from typing import Annotated, Union from fastapi import Cookie, FastAPI app = FastAPI() @app.get(“/items/”) async def read_items(ads_id: Annotated[Union[str, None], Cookie()] = None): return {“ads_id”: ads_id} {% endhighlight %} Header Header 데이터를 사용할 수 있습니다. Header의 경우 "-"이 "_"으로 자동 치환이 되기때문에 원본을 사용하려면 옵션을 추가해줘야 합니다. {% highlight python %} from typing import Annotated, Union from fastapi import FastAPI, Header app = FastAPI() @app.get(“/items/”) async def read_items(user_agent: Annotated[Union[str, None], Header(convert_underscores=False)] = None): return {“User-Agent”: user_agent} {% endhighlight %} Form Form data의 경우 우선 pip install python-multipart를 필요로 합니다. Form data는 아래와 같이 활용이 가능합니다. {% highlight python %} from typing import Annotated from fastapi import FastAPI, Form app = FastAPI() @app.post(“/login/”) async def login(username: Annotated[str, Form()], password: Annotated[str, Form()]): return {“username”: username} {% endhighlight %} File File data의 경우 우선 pip install python-multipart를 필요로 합니다. File data는 2가지 방식으로 활용 가능합니다. bytes: 이미지의 원본을 binary값으로 가져옵니다. UploadFile: 이미지의 관련 정보들을 가져옵니다. filename content_type file read write seek close {% highlight python %} from typing import Annotated from fastapi import FastAPI, File, UploadFile app = FastAPI() @app.post(“/files/”) async def create_file(file: Annotated[bytes, File()]): return {“file_size”: len(file)} @app.post(“/uploadfile/”) async def create_upload_file(file: Annotated[UploadFile, File()]): return {“filename”: file.filename} {% endhighlight %} Next Next
PYTHON
/
Framework
/
FastAPI
· 2024-02-01
Django init summary
Django django-debug-toolbar -> 디버그 내용 확인 용이 INSTALLED_APPS “debug_toolbar” MIDDLEWARE “debug_toolbar.middleware.DebugToolbarMiddleware” INTERNAL_IPS “IP” djangorestframework -> restAPI 제작에 용이 django-ninja -> djangorestframework의 느린 serializer 대신 pydantic 사용 swagger 지원 POSTMAN 같은것 Async 지원 api version 지원 django-seed -> db에 임시 데이터 뿌려줌 INSTALLED_APPS “django_seed” cmd: python manage.py seed <app_name> --number=<num> 서버 기본 세팅 {% highlight shell %} 프로젝트 생성 구문 django-admin startproject 프로젝프 앱 생성 구문 cd python /manage.py startapp 프로젝프 슈퍼 유저 생성 python /manage.py createsuperuser 프로젝프 기본 마이그레이션 python /manage.py migrate 프로젝프 추가 제작한 마이그레이션 (오류발생시 setting.py -> INSTALLED_APPS에 추가) python /manage.py makemigrations 서버 구동 구문 python \manage.py runserver {% endhighlight %} 유저 테이블 제작(models) 커스텀 유저 테이블을 제작하는데 있어서 직접 제작이 가능하며 아래의 방법들이 있다. 추상화 유저 제작 {% highlight python %} from django.contrib.auth.models import AbstractUser class Users(AbstractUser): pay_plan = models.ForeignKey(PayPlan, on_delete=models.DO_NOTHING) {% endhighlight %} setting.py에 AUTH_USER_MODEL = ‘<app_name>.Users’ 추가해야함 장고 모듈 활용 유저 제작 {% highlight python %} from django.contrib.auth.models import User as U class UserDetail(models.Model): user = models.OneToOneField(U, on_delete=models.CASCADE) pay_plan = models.ForeignKey(PayPlan, on_delete=models.DO_NOTHING) {% endhighlight %} 두개를 모두 사용할 경우 oneToOneField의 입력이 추상화의 Users를 가르켜야한다. view_html urls.py url 패턴을 이용하여 입력받는 주소로 부터 사용할 view단과 연결을 해준다. {% highlight python %} from app_test.views import index, redirect_test urlpatterns = [ path(‘’, index , name=’index’), path(‘redirect’, redirect_test), ] {% endhighlight %} view.py urls.py에 연결된 view단을 세팅하는 부분 base.html과 같은경우는 <app_name>/templates 폴더에 둔다. {% highlight python %} 모델(DB)에서 참조할시 해당 모델 추가사항 from .models import Users 모델에서 유저정보를 받아서 보여지는 결과를 다르게 한 예시 def index(request): user = Users.objects.filter(username=”admin”).first() email = user.email if user else “Nooooo” print(email) print(request.user.is_authenticated) if not request.user.is_authenticated: email = “NOoooooooooooooo” print(email) return render(request, “base.html”, {“welcome_msg”: f”Hello {email}”}) 재참조 형식의 view단 예시 def redirect_test(request): return redirect(“index”) {% endhighlight %} view_restAPI urls.py url 패턴을 이용하여 입력받는 주소로 부터 사용할 view단과 연결을 해준다. <int:user_id>는 query string 사용법 {% highlight python %} urlpatterns = [ path(‘get_user/', get_user), ] {% endhighlight %} view.py urls.py에 연결된 view단을 세팅하는 부분 csrf_exempt는 token 인증을 임시로 풀기위한것 body가 아닌 JsonResponse는 보통 개발자들끼리 내용을 주고 받을때 사용하기도 한다. {% highlight python %} from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt @csrf_exempt def get_user(request, user_id): print(user_id) if request.method == “GET”: abc = request.GET.get(“abc”) xyz = request.GET.get(“xyz”) user = Users.objects.filter(pk=user_id).first() return render (request, “base.html”, {“user”: user, “params”: [abc, xyz]}) elif request.method == “POST”: username = request.GET.get(“username”) if username: user = Users.objects.filter(pk=user_id).update(username=username) return JsonResponse(status=201, data=dict(msg=”POST is”)) {% endhighlight %} admin_handling1 admin.py admin page에서 model을 핸들링 가능하게 해준다. {% highlight python %} from .models import PayPlan Register your models here. admin.site.register(PayPlan) {% endhighlight %} django original login setting urls.py 세팅 views.py {% highlight python %} from django.contrib.auth import authenticate, login from .forms import RegisterForm def register(request): if request.method == “POST”: form = RegisterForm(request.POST) msg = “not valid data” if form.is_valid(): form.save() # db에 저장(commit=False로 임시저장가능) username = form.cleaned_data.get(“username”) raw_password = form.cleaned_data.get(“password1”) user = authenticate(username= username, password= raw_password) # 인증 login(request, user) # 로그인 msg = “login” return render(request, “register.html”, {“form”: form, “msg”: msg}) else: form = RegisterForm() return render(request, “register.html”, {“form”: form}) {% endhighlight %} forms.py {% highlight python %} from django import forms from django.contrib.auth.forms import UserCreationForm from .models import Users class RegisterForm(UserCreationForm): username = forms.CharField(max_length=20, required=True, help_text=”name”, label=”이름”) email = forms.EmailField(max_length=20, required=False, help_text=”email”, label=”이메일”) class Meta: model = Users fields = ( "username", "email", "password1", "password2", ) {% endhighlight %} paginator views.py 페이지 분할 제작법 {% highlight python %} from django.core.paginator import Paginator def a(request): page = int(request.GET.get(“page”, 1)) = <>.objects.all().order_by("-id") # 정렬해야 "-"내림차순 paginator = Paginator(, 5) = paginator.get_page(page) return render(request, ".html", {"args": <datas>}) {% endhighlight %} login_required setting.py LOGIN_URL =”/<>” views.py 로그인 필수 페이지 세팅 {% highlight python %} from django.contrib.auth.decorators import login_required @login_required def a(request): {% endhighlight %} template tags 탬플렛을 제작하는데 있어서 조건이나 데이터 활용등을 위한 태그 {\% csrf_token \%} csrf token을 필요로 하다는것을 나타낼때 사용 {\% cycle “a” “b” \%} iter적인 상황에서 각 순서마다 서로 다른 영향을 주려고 할때 사용 {\% extends “.html" \%} 한 html에서 다른 html을 확장하여 사용할때 사용 자식페이지에 최상단에 필요하며 은 부모파일이다. {\% block \%} {\% endblock \%} 한 html에서 다른 html을 확장하여 사용할때 사용 부모페이지에서 자식을 넣을공간에, 자식페이지에서 넣을 내용을 감싼다. {\% if \%}{\% elsif \%}{\% else \%}{\% endif \%} 조건문을 사용할때 사용 {\% for i in items \%}{\% endfor \%} for문을 사용할때 사용 forloop.counter 루프의 인덱스(1시작) forloop.counter0 루프의 인덱스(0시작) forloop.first 루프의 첫번째 True forloop.last 루프의 마지막 True {\% includes “<filename>.html” \%} html에서 html 파일을 사용할때 사용할 <filename>을 작성 extends보다 느리다.(랜더링이 느리다.) {\% url “<namespace>” \%} <namespace>로 이동하게 할때 사용 {\% static “<filepath>” \%} <filepath>에 있는 static 파일 사용 <app_name>/static/<filepath>으로 구성 {\% load static \%}을 최상단에 작성해야함 custom tags 탬플렛을 제작하는데 있어서 <app_name>/templatetags/custom_tags.py {% highlight python %} from django import template from django.utils.html import mark_safe register = template.Library() @register.simple_tag(name=, takes_context=True) def tags_test(context): # context에는 해당 페이지 정보가 있으며 상세히 다룰 수 있다. tag_html = "태그" # mark_safe는 html로 사용해도 안전함을 알려줌 사용안하면 string으로 사용 return mark_safe(tag_html) {% endhighlight %} {\% load custom_tags \%} 태그 사용할 html 최상단에 적용 {{data|<tag_name>}} 태그 사용할 html 코드에 사용 custom filter 탬플렛을 제작하는데 있어서 원하는 내용만 보여주기 위하여 사용 <app_name>/templatetags/custom_filters.py {% highlight python %} from django import template register = template.Library() @register.filter(name=) def email_masker(value): email_split = value.split("@") return f"{email_split[0]}@******.***" {% endhighlight %} {\% load custom_filters \%} 필터 사용할 html 최상단에 적용 {{data|<filter_name>}} 필터 사용할 html 코드에 사용
PYTHON
/
Framework
/
Django
· 2023-08-25
Django unit test
What is serializer test test_base normal serializer 일반적인 seriallizer는 serializers.Serializer의 상속을 받아 아래와 같이 작성하게 됩니다. {% highlight python %} class UserSerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) name = serializers.CharField(required=False, allow_blank=True, max_length=10) def create(self, validated_data): return models.User.objects.create(**validated_data) {% endhighlight %} 위와 같은 방식은 model과 상관없이 serializer를 설정하고 싶을때 사용할 수 있으며, model을 활용하여 사용하는 방법도 있습니다. model serializer model seriallizer는 serializers.ModelSerializer의 상속을 받아 아래와 같이 작성하게 됩니다. 아래의 방식은 model이 선언되어 있을때 사용하기 용이하며 코드를 간편하게 해줍니다. {% highlight python %} class UserSerializer(serializers.ModelSerializer): class Meta: model = models.User fields = [‘id’, ‘name’] {% endhighlight %} method field method field는 다른 field를 활용하여 재가공하여 사용하는 방식입니다. 대표적으로 id값이 있고 id의 길이를 확인하는 방법입니다. {% highlight python %} class UserSerializer(serializers.ModelSerializer): is_long = serializers.SerializerMethodField() class Meta: model = models.User fields = ['id', 'name'] def get_is_long(self, instance: CheckedCrn): if len(instance.id) > 2: return "long" else: return "short" {% endhighlight %} dict -> json -> dict 때에 따라서는 데이터의 형태가 계속 변경이 되어야 할 수 있습니다. 그에따라 형변환을 하는 법을 나타냅니다. {% highlight python %} import io from rest_framework.renderers import JSONRenderer content = JSONRenderer().render(serializer.data) # json stream = io.BytesIO(content) data = JSONParser().parse(stream) serializer = UserSerializer(data=data) serializer.is_valid() serializer.validated_data # check data serializer.save() serializer.data # dict {% endhighlight %} nested serializer N in (1:N) 1:N 의 형식에서 N의 입장은 다음과 같이 바로 활용이 가능합니다. seriallizer {% highlight python %} class GroupOnlySerializer(serializers.ModelSerializer): class Meta: model = Group fields = “all” class UserWithGroupSerializer(serializers.ModelSerializer): product = GroupOnlySerializer(read_only=True) class Meta: model = User fields = "__all__" {% endhighlight %} view {% highlight python %} class UserListAPIView(generics.ListAPIView): “”” User GET ALL API “”” queryset = User.objects.all().order_by(“-id”) serializer_class = UserWithGroupSerializer {% endhighlight %} 1 in (1:N) 1:N 의 형식에서 1의 입장은 역참조를 해야 하기 때문에 조금은 복잡합니다. Read case seriallizer {% highlight python %} class GroupSerializer(serializers.ModelSerializer): class Meta: model = Group fields = “all” class UserSerializer(serializers.ModelSerializer): product = GroupSerializer(read_only=True, many=True) class Meta: model = User fields = "__all__" {% endhighlight %} view {% highlight python %} class UserListAPIView(generics.ListAPIView): “”” User GET ALL API “”” queryset = User.objects.all().order_by(“-id”) serializer_class = UserSerializer {% endhighlight %} Write case write case에서는 원래 read만 가능하기 때문에 create를 override하여 새로이 구성해줘야 합니다. seriallizer {% highlight python %} class GroupSerializer(serializers.ModelSerializer): class Meta: model = Group fields = “all” class UserSerializer(serializers.ModelSerializer): product = GroupSerializer(read_only=True, many=True) class Meta: model = User fields = "__all__" {% endhighlight %} view {% highlight python %} class UserListAPIView(generics.ListAPIView): “”” User GET ALL API “”” queryset = User.objects.all().order_by(“-id”) serializer_class = UserSerializer def create(self, validated_data): groups = validated_data.pop(‘group’) user = User.objects.create(**validated_data) for group in groups: Group.objects.create(user=user, **group) return user {% endhighlight %} Next step models serializers views Prev(how to start) Prev
PYTHON
/
Framework
/
Django
· 2023-01-01
Django web
W G {% highlight python %} {% endhighlight %} Next step models forms views Prev(how to start) Prev
PYTHON
/
Framework
/
Django
· 2023-01-01
<
>
Touch background to close