어제 하다하다 안됐던거 오늘 드디어 해결한거 같다.
api/_ _init__.py
에 있던 default_app_config
를 지웠고 (init.py은 빈파일)
ApiConfig 클래스(WeatherConfig라고 썼던거)는 api/apps.py
로 옮겼다. (broker.py
에서는 지움)
admin.py
에 WeatherDB의 ModelAdmin
도 추가를 했는데 얘는 의미가 있는지 잘모르겠다.
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.py
와 apps.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 함수 내부 코드만 모든 빌딩에 대해 도는 것으로 바꾼 다음에 몇몇 에러 고치고 주기를 조절하면 끝
# 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("************************")
# 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
까지 써줘야 값이 빈 새 객체를 에러 없이 만들 수 있었다.
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는 고유 수행번호로 겹치면 수행되지 않는다고 한다. (겹치면 에러 발생)