[Django] 서버 시작하자마자 주기적으로 불러온 데이터 DB에 저장하기 (2)

김재연·2022년 3월 25일
1

BEMS 날씨

목록 보기
5/5
post-custom-banner

AppConfig ready() 제대로 사용하기

어제 하다하다 안됐던거 오늘 드디어 해결한거 같다.

  1. api/_ _init__.py 에 있던 default_app_config 를 지웠고 (init.py은 빈파일)

  2. ApiConfig 클래스(WeatherConfig라고 썼던거)는 api/apps.py 로 옮겼다. (broker.py 에서는 지움)

  3. admin.py 에 WeatherDB의 ModelAdmin 도 추가를 했는데 얘는 의미가 있는지 잘모르겠다.

  4. ready() 메소드 안에서 모델과 함수 임포트하기

가장 중요한 핵심은 ApiConfig 내부의 ready() 메소드 안에서 모델과 사용할 함수를 임포트시켰다는거다.(4번) 원래 파일 가장 상단에 썼었는데 이것저것 돌리다보니 상단에 임포트하는거조차 오류를 나게 하는거 같아서(임포트만 하고 코드에서 사용을 안했는데도 오류남) 뺐더니 잘돌아갔다. 그럼 어디서 임포트를 시키냐? 해서 이것저것 찾아보니 ready() 안에서 임포트하는거 같길래 모델을 그 안에서 임포트시켰다. 사용할 함수는 별 상관없겠지 싶어서 상단에 박았더니 또 안되길래 얘도 안에 임포트시켰더니 잘됐다. 이유는 모르겠으나 중요한점은 장고 서버가 실행될때 한번 실행되는 메소드인 AppConfig의 ready()안에 사용할 모든 것들을 쓰고 임포트까지도 그 메소드 안에 포함시켜야된다는 거다.

Django AppConfig Ready 메소드 공식문서

from django.apps import AppConfig

class MyAppConfig(AppConfig):
    def ready(self):
        # importing model classes
        from .models import MyModel  # 여기서 모델과 사용할 함수를 임포트 시켜야 했다.
        # 여기를 override해서 장고가 시작될때 실행할 코드를 작성한다.

(기쁨의) 최종코드

최종코드는 이거다. 코드 정리를 한번 해서 broker.py 는 삭제했고 여기 있던 코드들은 views.pyapps.py 에 나눠 넣었다.

# api/apps.py
from django.apps import AppConfig

class ApiConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'api'
    def ready(self):
        from .models import BemsBuilding, WeatherDB
        from .views import cron_weather
        cron_weather()
# api/views.py
from api.models import BemsBuilding, WeatherDB
from apscheduler.schedulers.background import BackgroundScheduler
from .weather_api import check_weather
import time

# ... def get_building() ...

def job():
    print(f'******{time.strftime("%H:%M:%S")}******')

    building = get_building(pk=1)
    data = check_weather(55, 127)
    weather = WeatherDB.objects.get(building=building)
    weather.temp = data['tmp']
    weather.humidity = data['hum']
    weather.rainType = data['rain']
    weather.sky = data['sky']
    weather.save()

    print(weather)

    print("************************")

def cron_weather():
    sched = BackgroundScheduler()
    # interval - 일정주기로 수행(테스트용 10초)
    sched.add_job(job, 'interval', seconds=10, id='cron_weather')
    sched.start()
# api/__init__.py
# 빈파일
# api/admin.py
from django.contrib import admin
from .models import BemsBuilding, WeatherDB

# Register your models here.
class BemsBuildingAdmin(admin.ModelAdmin):
    list_display = [f.name for f in BemsBuilding._meta.fields]

class WeatherDBAdmin(admin.ModelAdmin):
    list_display = [f.name for f in WeatherDB._meta.fields]

admin.site.register(BemsBuilding, BemsBuildingAdmin)
admin.site.register(WeatherDB, WeatherDBAdmin)

테스트 돌려보기

데이터가 하나도 없으면 오류가 나서 일단 임시 데이터를 이렇게 만들고

서버를 돌리면 10초마다 api를 받아와서 값을 업데이트한다.

그러면 admin 페이지에서 건물 weatherDB 정보가 이렇게 받아온 값으로 바뀐다.


마지막

job 함수 내부 코드만 모든 빌딩에 대해 도는 것으로 바꾼 다음에 몇몇 에러 고치고 주기를 조절하면 끝

1. 모든 빌딩에 대해 불러오기

# api/views.py
def job():
    print(f'******{time.strftime("%H:%M:%S")}******')
	# job 내부 동작만 바꾸면 됨
    buildings = BemsBuilding.objects.all()
    for building in buildings:
        nx, ny = mapToGrid(building.lat, building.lon)
        data = check_weather(nx, ny)
        weather, created = WeatherDB.objects.get_or_create(building=building)
        weather.temp = data['tmp']
        weather.humidity = data['hum']
        weather.rainType = data['rain']
        weather.sky = data['sky']
        weather.save()

    print("weatherDB saved")
    print("************************")

2. 날씨 객체 없으면 만들어서 저장하기

# api/views.py
weather, created = WeatherDB.objects.get_or_create(building=building)
# api/models.py
class WeatherDB(models.Model):
    building = models.ForeignKey('BemsBuilding', on_delete=models.CASCADE, null=True, blank=True)
    timestamp = models.DateTimeField(auto_now=True, null=True, blank=True)
    temp = models.IntegerField(blank=True, null=True)
    humidity = models.IntegerField(blank=True, null=True)
    rainType = models.CharField(max_length=20, blank=True, null=True)
    sky = models.IntegerField(blank=True, null=True)

    def __str__(self):
        return str(self.building) + " - " + str(self.timestamp)

get_or_create 만 쓰면 되는 줄 알았는데 모델에서 blank=True, null=True 까지 써줘야 값이 빈 새 객체를 에러 없이 만들 수 있었다.

3. 스케줄러 주기 조절하기

def cron_weather():
    sched = BackgroundScheduler()
    # 10초마다 실행
    sched.add_job(job, 'interval', seconds=10, id='cron_weather')
    # 1분마다 실행
    sched.add_job(job, 'interval', minutes=1, id='cron_weather')
    # 30분마다 실행
    sched.add_job(job, 'interval', minutes=30, id='cron_weather')
    # 매시간 59초 10초에 실행
    sched.add_job(job, 'cron', minute="59", second='10', id="cron_weather")
    sched.start()

interval에는 상수값으로 넣지만 cron에는 문자열로 넣는다는 차이점이 있다. 그리고 interval에는 second's', minute's'지만 cron에서는 그냥 s 빼고 second, minute 이다. id는 고유 수행번호로 겹치면 수행되지 않는다고 한다. (겹치면 에러 발생)


전체코드(최종)

https://github.com/chaeri93/bems

profile
일기장같은 공부기록📝
post-custom-banner

0개의 댓글