Django 프로젝트 - 1주차

Red_Panda·2021년 7월 29일
0

Python & Django

목록 보기
3/7

Django 프로젝트를 하기로 한지 약 1주일이 지났다.

프로젝트 내용은 이렇다.

냉장고에 남는 자투리 음식들이나 재료들이 이것저것 있는데 이것들을 가지고 뭘 해먹을 수 있을지 일일이 매번 검색하기가 귀찮았다. 그래서 사이트에 레시피들을 등록해놓고 내가 가진 것들로 뭘 만들어 먹을 수 있을지 레시피를 알려주는 서비스? 사이트를 만들기로했다.

결론은 내가 필요해서 만들기 시작하는 것이다. 후후후

2021.7.22

개발환경을 세팅했다.
Django를 설치하고, MySQL을 연동했다.

Django에서 MySQL을 연동하는 방법은 매우 간단했다. setting.py파일에서 간단하게 설정해주면 됐다.

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': '연동할 mysql db 이름',
        'USER': 'root 접속 계정 이름',
        'PASSWORD': password,
        'HOST': 'localhost',
        'PORT': '3306',
    }
}

이렇게 설정해주고

python manage.py makemigrations
python manage.py migrate

를 거쳐주면 연동된다. 오픈된 공간에 올릴꺼면 password, SECRET_KEY 같은 경우는 다른 올리지 않을 파일에 입력하여 연결시켜주는게 좋다.

MySQL 루트관리자 비밀번호를 까먹어서 시간이 걸렸지만..
그리고 DRF도 세팅 해보고 화면 나오는것까지 확인했다.

(이 후에, 레시피등록 기능을 만들었는데 여기서 간단하게 테스트 할 수 있었다.)

Django 입문 시작!


2021.7.23

오늘은 main페이지를 어떻게 구성할지 다시 생각해봤다.

먼저 레시피들이 필요하다.
레시피 모델을 세우고, 장고 admin을 이용해 데이터를 생성하고 잘 출력되는지 확인했다.
근데 가장 중요한 재료로 검색하는 경우를 생각해서 어떻게 테이블을 구성 해야할지 고민중이다.
일단은 레시피를 등록할때 재료 입력시 룰을 정해 공백없이 ,로 구분하여 입력 후 split을 이용해보기로 했다.

등록된 레시피 재료가 아래와 같을때
Recipe 1 : 라면,파,계란
Recipe 2 : 밥,계란,파,소고기
입력한 재료 : 라면,계란,파,밥,돼지고기

먼저 입력한 재료를 쪼개준다. ['라면','계란','파','밥','돼지고기]
그리고 이 쪼갠값들을 각 레시피 재료를 쪼갠값과 비교한다.
Recipe 1 = ['라면','파','계란'] 각 재료가 입력한 재료값안에 있는지 체크
-> 모두 있다 -> 출력
Recipe 2 = ['밥','계란','파','소고기'] 밥, 계란, 파는 존재하지만 소고기는 존재하지않는다 -> 재료가 부족하므로 출력하지 않는다.

뭔가.. 구현하지도 않았지만 일일이 레시피내 재료와 비교하자니 계산이 오래 걸릴 것같다. 더 좋은 방법을 찾아야겠다. 만약 더 좋은 방법이 생각나지 않으면 일단 이렇게라도 기능을 구현해볼 생각이다.

그리고, 등록된 레시피 데이터들을 html을 이용해 출력하도록 했다.


2021.7.24~25

주말에는 놀려했지만 심심해서 조금이나마 기능을 추가해봤다.
먼저 각 레시피들에 댓글+평점을 남길 수 있는 리뷰모델을 추가했다.

class Review(models.Model):
    recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
    content = models.TextField()
    create_date = models.DateTimeField()
    score = models.IntegerField(null=False, validators=[MaxValueValidator(5),MinValueValidator(1)])

점수는 1~5점까지 줄수있다. 그리고 이 점수들을 계산해서 평점을 나타내면 좋겠다고 생각해 방법을 조사했다.
Django에는 annotate라는 아주 좋은 기능이 있었다.
이걸 이용하면 아주 간단하게 필드를 추가할 수 있다.

from django.db.models import Count, Avg
def index(request):
	recipe_list = Recipe.objects.all().order_by('-create_date')\
    	    .annotate(reviews_count = Count('review'))\
        	.annotate(score_avg=Round(Avg('review__score')))

위와 같이 해주면 recipe에 reviews_count, score_avg 필드를 추가할 수 있다.

.annotate(reviews_count = Count('review')) # 외래키로 연결된 review의 개수를 count해준다.
.annotate(score_avg=Round(Avg('review__score')) # 외래키로 연결된 review의 score 평균을 구해준다.

Round값은 평균값이 소수점으로 인해 너무 길어짐을 방지 하기위해 넣었다. 단 Round는 바로 사용할 수 있는게 아니다.
커스텀 db 함수를 만들때 사용하는 Func를 사용해야한다.

class Round(Func): # Avg 함수 값 결과를 소수점 1자리까지만 출력
    function = 'ROUND' # sql 함수
    template='%(function)s(%(expressions)s, 1)'
    # 질의할 query를 만드는 템플릿, 기본값 : '%(function)s(%(expressions)s)'

from django.db.models import Func 를 이용해 위와 같이 ROUND 함수를 만들어줘야 사용할 수 있다.

그리하여 위와 평점 4.142857142857143을 소수점 한자리로 깔끔하게 나타낼 수 있었다.

2021.7.26~27

오늘은 이미지 등록에 관해 공부했다.

아래와 같은 레시피 등록 form을 만들고 등록했는데 이미지가 반영이 되지 않았다. 근데 admin에서 등록할때는 main화면 레시피 리스트에서 이미지가 잘 뜬다.
계속된 검색으로 해결방법을 찾을 수 있었다.. 매우 간단한 문제였다.

def recipe_create(request):
    if request.method == 'POST':
        form = RecipeForm(request.POST, request.FILES)
        # 이미지 등 파일 업로드시에는 request.FILES 추가 해야함!!
        if form.is_valid():
            recipe = form.save(commit=False)
            recipe.create_date = timezone.now()
            recipe.save()
            return redirect('naengpa:index')
    else: # 레시피 등록하기 GET방식 <a href=>링크 통한 요청은 GET
        form = RecipeForm()
    context = {'form':form}
    return render(request,'naengpa/recipe_create_form.html', context)

위가 레시피 등록함수인데 request.FILES 하나 추가해줬더니.. 잘 반영됐다.
그런데 등록한 이미지를 그대로 출력하자니 크기가 제각각이고, 큰 용량의 이미지도 그대로 저장됐다. 큰 용량의 이미지를 그대로 저장하면 나중에 서버에 부담이 된다.

그래서 이미지를 등록할때 사용하는 라이브러리를 찾았다.

from imagekit.models import ProcessedImageField
from imagekit.processors import ResizeToFill

아래 코드는 RECIPE 모델에 위 라이브러리를 적용한 image에 대한 내용이다.

image = ProcessedImageField(
        upload_to='',
        processors=[ResizeToFill(400,300)], # 처리작업 이미지 400,300으로 리사이징
        format='JPEG', # 저장 포맷
        options= {'quality': 80}, # 압축률 설정
        null=False,
        blank=False,
        default='',
    )

ResizeToFill은 내가 올린 이미지 크기가 어떻든 400x300크기로 바꿔준다.
그리고 options를 통해 압축률을 낮춰 용량을 낮추고 저장할수있다.
upload_to는 등록한 이미지가 어디에 저장될 것인지 정한다.

이렇게 저장 후, 레시피 내용을 볼때 400x300인 db에 저장한 사진을 출력하고, 레시피 리스트에서는 해당사진을 html내에서 적절한 크기로 조정해 출력했다.

레시피 내용에서 크게 나오는 모습

레시피 리스트에서 작게 나오는 모습


2021.7.28

오늘은 카테고리를 선택해 출력하는 기능을 구현했다.
먼저 카테고리는 한식, 양식, 일식, 중식으로 정했다.

그러기 위해서는 먼저 카테고리 체크박스를 만들고, 체크된 체크박스들의 값을 모두 가져와야했다.

<form action="{% url 'naengpa:index' %}" method="POST">
    {% csrf_token %}
    <div class="btn-group-toggle border-bottom py-3">
        <label class="btn btn-primary mr-2" for="ca1" data-toggle="button">
            <input type="checkbox" class="btn-check" name="category[]" id="ca1" value="ko" autocomplete="on">한 식
        </label>
        <label class="btn btn-primary mr-2" for="ca2">
            <input type="checkbox" class="btn-check" name="category[]" id="ca2" value="west" autocomplete="on">양 식
        </label>
        <label class="btn btn-primary mr-2" for="ca3">
            <input type="checkbox" class="btn-check" name="category[]" id="ca3" value="jp">일 식
        </label>
        <label class="btn btn-primary mr-2" for="ca4">
            <input type="checkbox" class="btn-check" name="category[]" id="ca4" value="cn">중 식
        </label>

    <button type="submit" class="btn btn-success">카테고리 검색하기</button>
    </div>
</form>

이렇게 체크박스를 추가했는데 레이블로 감싸니까 체크 유무를 확인할 수 없었다. 그래서 bootstrap에서 봤는데 data-toggle을 이용하면 된다고한다.
(쓰다가 발견한건데 data-toggle="button" 속성을 label에 넣어주면 되는거였다.)

이렇게 하고, 체크박스의 값들은 getlist를 이용해 가져올 수 있다고한다. 그래서 이 getlist를 이용해 아래와 같이 코드를 넣어 값을 잘 가져오는지 확인했다.

selected_cate = request.POST.getlist('category[]') # 체크박스 name
    if selected_cate: # 체크박스에서 가져온 값이 있다면
        query = Q() # django orm 에서 where에 조건을 추가하고싶을때 사용.
        # from django.db.models import Q 필요
        
        for i in selected_cate:
            query = query | Q(category__icontains=i)
            # 쿼리에 or을 이용해 recipe.category에 i값이 있는 쿼리들만 더해나갔다.
            print(query)
            
        recipe_list = recipe_list.filter(query) # 위에서 얻어낸 query를 이용하여 필터를 걸면 체크된 카테고리에 속하는 레시피들만 출력된다!
    else:
        print("선택된 카테고리 없음")

처음에 값을 제대로 가져오지 못했을때 자꾸 계속 이렇게 선택된게 없다고 떴었다..
이 부분에서 시간을 꽤 썼다.
끝없는 구글링와 이것저것을 시도를 한 끝에 아래와 같이 값을 가져오는 것을 발견할 수 있었다. (문제의 코드를 적어놓질 않아서 뭐가 문제였는지 지금 정확히 기억이 안난다.)

체크박스를 선택하고 submit 버튼을 누르면 위처럼 체크된 박스의 value값을 얻고 이 value값이 recipe.category에 포함된 row들만 출력하면된다.
category__icontains = i 는 recipe의 category에서 i가 속한 row만 찾는기능이다. (대소문자 구분x)

그 덕에 체크박스를 어떻게 선택해도 원하는 대로 출력할 수 있었다~


지금까지의 결과물이다. 디자인은 허접하지만 나름 뿌듯하다.

일주일동안 django를 맛봤는데 flask때와는 다르게 필요한 기능을 찾으면 이미 다있다. 그리고 자료도 훨씬 많다. 장고 짱짱.

이번주는 프로젝트 시작 첫 주라 변화가 많아 재미있어서 프로젝트만 했던 것같다. 특히 몇시간 삽질끝에 성공하는 그 희열은 크..

앞으로 추가해야할 것도 많지만 cs와 코테도 챙겨야해서 지금보단 더 느린 속도로 진행될듯싶다~

profile
신입 개발자

0개의 댓글