PR fixes
This commit is contained in:
parent
7bcefeb347
commit
1be947e946
13 changed files with 175 additions and 206 deletions
|
@ -1,7 +1,6 @@
|
|||
<div class='pool-input'>
|
||||
<div class='main-control'>
|
||||
<input type='text' placeholder='type to add…'/>
|
||||
<!-- <button>Add</button> -->
|
||||
</div>
|
||||
|
||||
<ul class='compact-pools'></ul>
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
</section>
|
||||
|
||||
<section>
|
||||
Aliases:<br/>
|
||||
<ul><!--
|
||||
--><% for (let name of ctx.pool.names.slice(1)) { %><!--
|
||||
--><li><%= ctx.makePoolLink(ctx.pool.id, false, false, ctx.pool, name) %></li><!--
|
||||
--><% } %><!--
|
||||
--></ul>
|
||||
Aliases:<br/>
|
||||
<ul>
|
||||
<% for (let name of ctx.pool.names.slice(1)) { %>
|
||||
<li><%= ctx.makePoolLink(ctx.pool.id, false, false, ctx.pool, name) %></li>
|
||||
<% } %>
|
||||
</ul>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
|
|
|
@ -54,8 +54,7 @@ class PoolController {
|
|||
this._view.addEventListener('submit', e => this._evtUpdate(e));
|
||||
this._view.addEventListener('merge', e => this._evtMerge(e));
|
||||
this._view.addEventListener('delete', e => this._evtDelete(e));
|
||||
},
|
||||
error => {
|
||||
}, error => {
|
||||
this._view = new EmptyView();
|
||||
this._view.showError(error.message);
|
||||
});
|
||||
|
@ -68,9 +67,7 @@ class PoolController {
|
|||
_evtSaved(e, section) {
|
||||
misc.disableExitConfirmation();
|
||||
if (this._name !== e.detail.pool.names[0]) {
|
||||
router.replace(
|
||||
uri.formatClientLink('pool', e.detail.pool.id, section),
|
||||
null, false);
|
||||
router.replace(uri.formatClientLink('pool', e.detail.pool.id, section), null, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,9 +84,9 @@ class PoolController {
|
|||
e.detail.pool.description = e.detail.description;
|
||||
}
|
||||
if (e.detail.posts !== undefined) {
|
||||
e.detail.pool.posts.clear()
|
||||
e.detail.pool.posts.clear();
|
||||
for (let post_id of e.detail.posts) {
|
||||
e.detail.pool.posts.add(Post.fromResponse({ id: parseInt(post_id) }))
|
||||
e.detail.pool.posts.add(Post.fromResponse({ id: parseInt(post_id) }));
|
||||
}
|
||||
}
|
||||
e.detail.pool.save().then(() => {
|
||||
|
|
|
@ -16,8 +16,7 @@ class PoolCreateController {
|
|||
return;
|
||||
}
|
||||
|
||||
PoolCategoryList.get()
|
||||
.then(poolCategoriesResponse => {
|
||||
PoolCategoryList.get().then(poolCategoriesResponse => {
|
||||
const categories = {};
|
||||
for (let category of poolCategoriesResponse.results) {
|
||||
categories[category.name] = category.name;
|
||||
|
|
|
@ -81,105 +81,105 @@ class PoolInputControl extends events.EventTarget {
|
|||
return Promise.resolve();
|
||||
}
|
||||
|
||||
this.pools.add(pool, false)
|
||||
this.pools.add(pool, false)
|
||||
|
||||
const listItemNode = this._createListItemNode(pool);
|
||||
if (!pool.category) {
|
||||
listItemNode.classList.add('new');
|
||||
}
|
||||
this._poolListNode.prependChild(listItemNode);
|
||||
_fadeOutListItemNodeStatus(listItemNode);
|
||||
const listItemNode = this._createListItemNode(pool);
|
||||
if (!pool.category) {
|
||||
listItemNode.classList.add('new');
|
||||
}
|
||||
this._poolListNode.prependChild(listItemNode);
|
||||
_fadeOutListItemNodeStatus(listItemNode);
|
||||
|
||||
this.dispatchEvent(new CustomEvent('add', {
|
||||
detail: {pool: pool, source: source},
|
||||
}));
|
||||
this.dispatchEvent(new CustomEvent('change'));
|
||||
this.dispatchEvent(new CustomEvent('add', {
|
||||
detail: {pool: pool, source: source},
|
||||
}));
|
||||
this.dispatchEvent(new CustomEvent('change'));
|
||||
|
||||
return Promise.resolve();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
deletePool(pool) {
|
||||
if (!this.pools.hasPoolId(pool.id)) {
|
||||
return;
|
||||
deletePool(pool) {
|
||||
if (!this.pools.hasPoolId(pool.id)) {
|
||||
return;
|
||||
}
|
||||
this.pools.removeById(pool.id);
|
||||
this._hideAutoComplete();
|
||||
|
||||
this._deleteListItemNode(pool);
|
||||
|
||||
this.dispatchEvent(new CustomEvent('remove', {
|
||||
detail: {pool: pool},
|
||||
}));
|
||||
this.dispatchEvent(new CustomEvent('change'));
|
||||
}
|
||||
this.pools.removeById(pool.id);
|
||||
this._hideAutoComplete();
|
||||
|
||||
this._deleteListItemNode(pool);
|
||||
_createListItemNode(pool) {
|
||||
const className = pool.category ?
|
||||
misc.makeCssName(pool.category, 'pool') :
|
||||
null;
|
||||
|
||||
this.dispatchEvent(new CustomEvent('remove', {
|
||||
detail: {pool: pool},
|
||||
}));
|
||||
this.dispatchEvent(new CustomEvent('change'));
|
||||
}
|
||||
const poolLinkNode = document.createElement('a');
|
||||
if (className) {
|
||||
poolLinkNode.classList.add(className);
|
||||
}
|
||||
poolLinkNode.setAttribute(
|
||||
'href', uri.formatClientLink('pool', pool.names[0]));
|
||||
|
||||
_createListItemNode(pool) {
|
||||
const className = pool.category ?
|
||||
misc.makeCssName(pool.category, 'pool') :
|
||||
null;
|
||||
const poolIconNode = document.createElement('i');
|
||||
poolIconNode.classList.add('fa');
|
||||
poolIconNode.classList.add('fa-pool');
|
||||
poolLinkNode.appendChild(poolIconNode);
|
||||
|
||||
const poolLinkNode = document.createElement('a');
|
||||
if (className) {
|
||||
poolLinkNode.classList.add(className);
|
||||
const searchLinkNode = document.createElement('a');
|
||||
if (className) {
|
||||
searchLinkNode.classList.add(className);
|
||||
}
|
||||
searchLinkNode.setAttribute(
|
||||
'href', uri.formatClientLink(
|
||||
'posts', {query: "pool:" + pool.id}));
|
||||
searchLinkNode.textContent = pool.names[0] + ' ';
|
||||
|
||||
const usagesNode = document.createElement('span');
|
||||
usagesNode.classList.add('pool-usages');
|
||||
usagesNode.setAttribute('data-pseudo-content', pool.postCount);
|
||||
|
||||
const removalLinkNode = document.createElement('a');
|
||||
removalLinkNode.classList.add('remove-pool');
|
||||
removalLinkNode.setAttribute('href', '');
|
||||
removalLinkNode.setAttribute('data-pseudo-content', '×');
|
||||
removalLinkNode.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
this.deletePool(pool);
|
||||
});
|
||||
|
||||
const listItemNode = document.createElement('li');
|
||||
listItemNode.appendChild(removalLinkNode);
|
||||
listItemNode.appendChild(poolLinkNode);
|
||||
listItemNode.appendChild(searchLinkNode);
|
||||
listItemNode.appendChild(usagesNode);
|
||||
for (let name of pool.names) {
|
||||
this._poolToListItemNode.set(name, listItemNode);
|
||||
}
|
||||
return listItemNode;
|
||||
}
|
||||
poolLinkNode.setAttribute(
|
||||
'href', uri.formatClientLink('pool', pool.names[0]));
|
||||
|
||||
const poolIconNode = document.createElement('i');
|
||||
poolIconNode.classList.add('fa');
|
||||
poolIconNode.classList.add('fa-pool');
|
||||
poolLinkNode.appendChild(poolIconNode);
|
||||
|
||||
const searchLinkNode = document.createElement('a');
|
||||
if (className) {
|
||||
searchLinkNode.classList.add(className);
|
||||
_deleteListItemNode(pool) {
|
||||
const listItemNode = this._getListItemNode(pool);
|
||||
if (listItemNode) {
|
||||
listItemNode.parentNode.removeChild(listItemNode);
|
||||
}
|
||||
for (let name of pool.names) {
|
||||
this._poolToListItemNode.delete(name);
|
||||
}
|
||||
}
|
||||
searchLinkNode.setAttribute(
|
||||
'href', uri.formatClientLink(
|
||||
'posts', {query: "pool:" + pool.id}));
|
||||
searchLinkNode.textContent = pool.names[0] + ' ';
|
||||
|
||||
const usagesNode = document.createElement('span');
|
||||
usagesNode.classList.add('pool-usages');
|
||||
usagesNode.setAttribute('data-pseudo-content', pool.postCount);
|
||||
|
||||
const removalLinkNode = document.createElement('a');
|
||||
removalLinkNode.classList.add('remove-pool');
|
||||
removalLinkNode.setAttribute('href', '');
|
||||
removalLinkNode.setAttribute('data-pseudo-content', '×');
|
||||
removalLinkNode.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
this.deletePool(pool);
|
||||
});
|
||||
|
||||
const listItemNode = document.createElement('li');
|
||||
listItemNode.appendChild(removalLinkNode);
|
||||
listItemNode.appendChild(poolLinkNode);
|
||||
listItemNode.appendChild(searchLinkNode);
|
||||
listItemNode.appendChild(usagesNode);
|
||||
for (let name of pool.names) {
|
||||
this._poolToListItemNode.set(name, listItemNode);
|
||||
_getListItemNode(pool) {
|
||||
return this._poolToListItemNode.get(pool.names[0]);
|
||||
}
|
||||
return listItemNode;
|
||||
}
|
||||
|
||||
_deleteListItemNode(pool) {
|
||||
const listItemNode = this._getListItemNode(pool);
|
||||
if (listItemNode) {
|
||||
listItemNode.parentNode.removeChild(listItemNode);
|
||||
_hideAutoComplete() {
|
||||
this._autoCompleteControl.hide();
|
||||
}
|
||||
for (let name of pool.names) {
|
||||
this._poolToListItemNode.delete(name);
|
||||
}
|
||||
}
|
||||
|
||||
_getListItemNode(pool) {
|
||||
return this._poolToListItemNode.get(pool.names[0]);
|
||||
}
|
||||
|
||||
_hideAutoComplete() {
|
||||
this._autoCompleteControl.hide();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PoolInputControl;
|
||||
|
|
|
@ -17,7 +17,7 @@ class PoolCategory extends events.EventTarget {
|
|||
|
||||
get name() { return this._name; }
|
||||
get color() { return this._color; }
|
||||
get poolCount() { return this._poolCount; }
|
||||
get poolCount() { return this._poolCount; }
|
||||
get isDefault() { return this._isDefault; }
|
||||
get isTransient() { return !this._origName; }
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ script_location = szurubooru/migrations
|
|||
|
||||
# overriden by szurubooru's config
|
||||
sqlalchemy.url =
|
||||
revision_environment = true
|
||||
|
||||
[loggers]
|
||||
keys = root,sqlalchemy,alembic
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
# shown in the website title and on the front page
|
||||
name: szurubooru
|
||||
# full url to the homepage of this szurubooru site, with no trailing slash
|
||||
domain: localhost # example: http://example.com
|
||||
domain: # example: http://example.com
|
||||
# used to salt the users' password hashes and generate filenames for static content
|
||||
secret: change
|
||||
|
||||
|
@ -49,9 +49,6 @@ enable_safety: yes
|
|||
tag_name_regex: ^\S+$
|
||||
tag_category_name_regex: ^[^\s%+#/]+$
|
||||
|
||||
pool_name_regex: ^\S+$
|
||||
pool_category_name_regex: ^[^\s%+#/]+$
|
||||
|
||||
# don't make these more restrictive unless you want to annoy people; if you do
|
||||
# customize them, make sure to update the instructions in the registration form
|
||||
# template as well.
|
||||
|
@ -61,72 +58,72 @@ user_name_regex: '^[a-zA-Z0-9_-]{1,32}$'
|
|||
default_rank: regular
|
||||
|
||||
privileges:
|
||||
'users:create:self': anonymous # Registration permission
|
||||
'users:create:any': administrator
|
||||
'users:list': regular
|
||||
'users:view': regular
|
||||
'users:edit:any:name': moderator
|
||||
'users:edit:any:pass': moderator
|
||||
'users:edit:any:email': moderator
|
||||
'users:edit:any:avatar': moderator
|
||||
'users:edit:any:rank': moderator
|
||||
'users:edit:self:name': regular
|
||||
'users:edit:self:pass': regular
|
||||
'users:edit:self:email': regular
|
||||
'users:edit:self:avatar': regular
|
||||
'users:edit:self:rank': moderator # one can't promote themselves or anyone to upper rank than their own.
|
||||
'users:delete:any': administrator
|
||||
'users:delete:self': regular
|
||||
'users:create:self': anonymous # Registration permission
|
||||
'users:create:any': administrator
|
||||
'users:list': regular
|
||||
'users:view': regular
|
||||
'users:edit:any:name': moderator
|
||||
'users:edit:any:pass': moderator
|
||||
'users:edit:any:email': moderator
|
||||
'users:edit:any:avatar': moderator
|
||||
'users:edit:any:rank': moderator
|
||||
'users:edit:self:name': regular
|
||||
'users:edit:self:pass': regular
|
||||
'users:edit:self:email': regular
|
||||
'users:edit:self:avatar': regular
|
||||
'users:edit:self:rank': moderator # one can't promote themselves or anyone to upper rank than their own.
|
||||
'users:delete:any': administrator
|
||||
'users:delete:self': regular
|
||||
|
||||
'user_tokens:list:any': administrator
|
||||
'user_tokens:list:self': regular
|
||||
'user_tokens:create:any': administrator
|
||||
'user_tokens:create:self': regular
|
||||
'user_tokens:edit:any': administrator
|
||||
'user_tokens:edit:self': regular
|
||||
'user_tokens:delete:any': administrator
|
||||
'user_tokens:delete:self': regular
|
||||
'user_tokens:list:any': administrator
|
||||
'user_tokens:list:self': regular
|
||||
'user_tokens:create:any': administrator
|
||||
'user_tokens:create:self': regular
|
||||
'user_tokens:edit:any': administrator
|
||||
'user_tokens:edit:self': regular
|
||||
'user_tokens:delete:any': administrator
|
||||
'user_tokens:delete:self': regular
|
||||
|
||||
'posts:create:anonymous': regular
|
||||
'posts:create:identified': regular
|
||||
'posts:list': anonymous
|
||||
'posts:reverse_search': regular
|
||||
'posts:view': anonymous
|
||||
'posts:view:featured': anonymous
|
||||
'posts:edit:content': power
|
||||
'posts:edit:flags': regular
|
||||
'posts:edit:notes': regular
|
||||
'posts:edit:relations': regular
|
||||
'posts:edit:safety': power
|
||||
'posts:edit:source': regular
|
||||
'posts:edit:tags': regular
|
||||
'posts:edit:thumbnail': power
|
||||
'posts:feature': moderator
|
||||
'posts:delete': moderator
|
||||
'posts:score': regular
|
||||
'posts:merge': moderator
|
||||
'posts:favorite': regular
|
||||
'posts:bulk-edit:tags': power
|
||||
'posts:bulk-edit:safety': power
|
||||
'posts:create:anonymous': regular
|
||||
'posts:create:identified': regular
|
||||
'posts:list': anonymous
|
||||
'posts:reverse_search': regular
|
||||
'posts:view': anonymous
|
||||
'posts:view:featured': anonymous
|
||||
'posts:edit:content': power
|
||||
'posts:edit:flags': regular
|
||||
'posts:edit:notes': regular
|
||||
'posts:edit:relations': regular
|
||||
'posts:edit:safety': power
|
||||
'posts:edit:source': regular
|
||||
'posts:edit:tags': regular
|
||||
'posts:edit:thumbnail': power
|
||||
'posts:feature': moderator
|
||||
'posts:delete': moderator
|
||||
'posts:score': regular
|
||||
'posts:merge': moderator
|
||||
'posts:favorite': regular
|
||||
'posts:bulk-edit:tags': power
|
||||
'posts:bulk-edit:safety': power
|
||||
|
||||
'tags:create': regular
|
||||
'tags:edit:names': power
|
||||
'tags:edit:category': power
|
||||
'tags:edit:description': power
|
||||
'tags:edit:implications': power
|
||||
'tags:edit:suggestions': power
|
||||
'tags:list': regular
|
||||
'tags:view': anonymous
|
||||
'tags:merge': moderator
|
||||
'tags:delete': moderator
|
||||
'tags:create': regular
|
||||
'tags:edit:names': power
|
||||
'tags:edit:category': power
|
||||
'tags:edit:description': power
|
||||
'tags:edit:implications': power
|
||||
'tags:edit:suggestions': power
|
||||
'tags:list': regular
|
||||
'tags:view': anonymous
|
||||
'tags:merge': moderator
|
||||
'tags:delete': moderator
|
||||
|
||||
'tag_categories:create': moderator
|
||||
'tag_categories:edit:name': moderator
|
||||
'tag_categories:edit:color': moderator
|
||||
'tag_categories:list': anonymous
|
||||
'tag_categories:view': anonymous
|
||||
'tag_categories:delete': moderator
|
||||
'tag_categories:set_default': moderator
|
||||
'tag_categories:create': moderator
|
||||
'tag_categories:edit:name': moderator
|
||||
'tag_categories:edit:color': moderator
|
||||
'tag_categories:list': anonymous
|
||||
'tag_categories:view': anonymous
|
||||
'tag_categories:delete': moderator
|
||||
'tag_categories:set_default': moderator
|
||||
|
||||
'pools:create': regular
|
||||
'pools:edit:names': power
|
||||
|
@ -146,19 +143,19 @@ privileges:
|
|||
'pool_categories:delete': moderator
|
||||
'pool_categories:set_default': moderator
|
||||
|
||||
'comments:create': regular
|
||||
'comments:delete:any': moderator
|
||||
'comments:delete:own': regular
|
||||
'comments:edit:any': moderator
|
||||
'comments:edit:own': regular
|
||||
'comments:list': regular
|
||||
'comments:view': regular
|
||||
'comments:score': regular
|
||||
'comments:create': regular
|
||||
'comments:delete:any': moderator
|
||||
'comments:delete:own': regular
|
||||
'comments:edit:any': moderator
|
||||
'comments:edit:own': regular
|
||||
'comments:list': regular
|
||||
'comments:view': regular
|
||||
'comments:score': regular
|
||||
|
||||
'snapshots:list': power
|
||||
'snapshots:list': power
|
||||
|
||||
'uploads:create': regular
|
||||
'uploads:use_downloader': power
|
||||
'uploads:create': regular
|
||||
'uploads:use_downloader': power
|
||||
|
||||
## ONLY SET THESE IF DEPLOYING OUTSIDE OF DOCKER
|
||||
#debug: 0 # generate server logs?
|
||||
|
|
|
@ -9,4 +9,3 @@ pillow>=4.3.0
|
|||
pynacl>=1.2.1
|
||||
pytz>=2018.3
|
||||
pyRFC3339>=1.0
|
||||
youtube_dl>=2020.5.3
|
||||
|
|
|
@ -34,22 +34,3 @@ def _bump_query_count() -> None:
|
|||
|
||||
|
||||
sa.event.listen(_engine, 'after_execute', lambda *args: _bump_query_count())
|
||||
|
||||
import time
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger("myapp.sqltime")
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
def before_cursor_execute(conn, cursor, statement,
|
||||
parameters, context, executemany):
|
||||
conn.info.setdefault('query_start_time', []).append(time.time())
|
||||
logger.info("Start Query: %s" % statement)
|
||||
|
||||
def after_cursor_execute(conn, cursor, statement,
|
||||
parameters, context, executemany):
|
||||
total = time.time() - conn.info['query_start_time'].pop(-1)
|
||||
logger.info("Total Time: %f" % total)
|
||||
|
||||
sa.event.listen(_engine, "before_cursor_execute", before_cursor_execute)
|
||||
sa.event.listen(_engine, "after_cursor_execute", after_cursor_execute)
|
||||
|
|
|
@ -195,5 +195,5 @@ def delete_category(category: model.PoolCategory) -> None:
|
|||
if (category.pool_count or 0) > 0:
|
||||
raise PoolCategoryIsInUseError(
|
||||
'Pool category has some usages and cannot be deleted. ' +
|
||||
'Please remove this category from relevant pools first..')
|
||||
'Please remove this category from relevant pools first.')
|
||||
db.session.delete(category)
|
||||
|
|
|
@ -77,6 +77,7 @@ def _duplicates(a: List[int]) -> List[int]:
|
|||
dupes.append(x)
|
||||
return dupes
|
||||
|
||||
|
||||
def sort_pools(pools: List[model.Pool]) -> List[model.Pool]:
|
||||
default_category_name = pool_categories.get_default_category_name()
|
||||
return sorted(
|
||||
|
@ -131,8 +132,7 @@ class PoolSerializer(serialization.BaseSerializer):
|
|||
|
||||
def serialize_posts(self) -> Any:
|
||||
return [post for post in
|
||||
[posts.serialize_micro_post(rel, None)
|
||||
for rel in self.pool.posts]]
|
||||
[posts.serialize_micro_post(rel, None) for rel in self.pool.posts]]
|
||||
|
||||
|
||||
def serialize_pool(
|
||||
|
@ -221,7 +221,7 @@ def merge_pools(source_pool: model.Pool, target_pool: model.Pool) -> None:
|
|||
if source_pool.pool_id == target_pool.pool_id:
|
||||
raise InvalidPoolRelationError('Cannot merge pool with itself.')
|
||||
|
||||
def merge_posts(source_pool_id: int, target_pool_id: int) -> None:
|
||||
def merge_pool_posts(source_pool_id: int, target_pool_id: int) -> None:
|
||||
alias1 = model.PoolPost
|
||||
alias2 = sa.orm.util.aliased(model.PoolPost)
|
||||
update_stmt = (
|
||||
|
@ -236,7 +236,7 @@ def merge_pools(source_pool: model.Pool, target_pool: model.Pool) -> None:
|
|||
update_stmt = update_stmt.values(pool_id=target_pool_id)
|
||||
db.session.execute(update_stmt)
|
||||
|
||||
merge_posts(source_pool.pool_id, target_pool.pool_id)
|
||||
merge_pool_posts(source_pool.pool_id, target_pool.pool_id)
|
||||
delete(source_pool)
|
||||
|
||||
|
||||
|
@ -304,8 +304,6 @@ def update_pool_posts(pool: model.Pool, post_ids: List[int]) -> None:
|
|||
assert pool
|
||||
dupes = _duplicates(post_ids)
|
||||
if len(dupes) > 0:
|
||||
print(str(dupes))
|
||||
print(str(post_ids))
|
||||
dupes = ', '.join(list(str(x) for x in dupes))
|
||||
raise InvalidPoolDuplicateError('Duplicate post(s) in pool: ' + dupes)
|
||||
ret = posts.get_posts_by_ids(post_ids)
|
||||
|
|
|
@ -300,7 +300,7 @@ class PostSerializer(serialization.BaseSerializer):
|
|||
self.post.comments,
|
||||
key=lambda comment: comment.creation_time)]
|
||||
|
||||
def serialize_pools(self) -> Any:
|
||||
def serialize_pools(self) -> List[Any]:
|
||||
return [
|
||||
pools.serialize_pool(pool)
|
||||
for pool in sorted(
|
||||
|
@ -343,7 +343,7 @@ def get_post_by_id(post_id: int) -> model.Post:
|
|||
return post
|
||||
|
||||
|
||||
def get_posts_by_ids(ids: List[int]) -> List[model.Pool]:
|
||||
def get_posts_by_ids(ids: List[int]) -> List[model.Post]:
|
||||
if len(ids) == 0:
|
||||
return []
|
||||
posts = (
|
||||
|
|
Loading…
Reference in a new issue